mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-11 05:38:31 +09:00
[Fix] (DirectGLES, ShaderTranspiler): widen the formats SPIRV-Cross refuses to print even where GL_NV_image_formats exists
This commit is contained in:
@@ -728,8 +728,6 @@ namespace MobileGL {
|
||||
LowerViewportIndexPass::DeclaresViewportIndexBuiltin(context.get());
|
||||
features.DeclaresMultisampledImage =
|
||||
ClampMultisampleFetchPass::DeclaresMultisampledImage(context.get());
|
||||
features.DeclaresWidenableImageFormat =
|
||||
WidenImageFormatsPass::DeclaresWidenableImageFormat(context.get());
|
||||
return features;
|
||||
}
|
||||
|
||||
@@ -776,10 +774,12 @@ namespace MobileGL {
|
||||
|
||||
bool ShaderCompiler::WidenImageFormatsForEssl(const Vector<Uint32>& inputBinary,
|
||||
Vector<uint32_t>& outputBinary,
|
||||
const bool onlyFormatsSpirvCrossRefusesToPrint,
|
||||
const bool enableSpirvValidation) {
|
||||
using namespace spvtools;
|
||||
Optimizer optimizer(SPV_ENV_VULKAN_1_1);
|
||||
optimizer.RegisterPass(WidenImageFormatsPass::CreateWidenImageFormatsPass());
|
||||
optimizer.RegisterPass(
|
||||
WidenImageFormatsPass::CreateWidenImageFormatsPass(onlyFormatsSpirvCrossRefusesToPrint));
|
||||
// Two image types that differed only in a format the widening collapses -
|
||||
// `layout(rg32f)` and `layout(rgba32f)` in one module - are one type afterwards,
|
||||
// and duplicate non-aggregate type declarations are invalid SPIR-V. This joins
|
||||
@@ -791,8 +791,10 @@ namespace MobileGL {
|
||||
true, enableSpirvValidation);
|
||||
}
|
||||
|
||||
bool ShaderCompiler::DeclaresWidenableImageFormat(const Vector<Uint32>& binary) {
|
||||
return WidenImageFormatsPass::DeclaresWidenableImageFormat(binary);
|
||||
bool ShaderCompiler::DeclaresWidenableImageFormat(const Vector<Uint32>& binary,
|
||||
const bool onlyFormatsSpirvCrossRefusesToPrint) {
|
||||
return WidenImageFormatsPass::DeclaresWidenableImageFormat(binary,
|
||||
onlyFormatsSpirvCrossRefusesToPrint);
|
||||
}
|
||||
|
||||
Uint ShaderCompiler::WidenedCoreEsslImageFormat(Uint glInternalFormat) {
|
||||
|
||||
@@ -71,10 +71,14 @@ namespace MobileGL {
|
||||
// GL_OES_viewport_array AND integer multisample squeezed to 1) the separate
|
||||
// probes made compile-heavy workloads measurably slower - ReservedNames-class
|
||||
// CTS cases paid ~10%. Callers with more than one armed gate use this instead.
|
||||
// The image-format widening deliberately does NOT ride this probe, even though it
|
||||
// is a module question of exactly the same shape. It is armed on every driver, so
|
||||
// a gate answered from the module would put a BuildModule on every stage of every
|
||||
// program - and the frontend's uniform reflection can answer it for free
|
||||
// (PrgramImpl::ImageFormatBakeInputs::declaresWidenableImageFormat).
|
||||
struct SpirvGateFeatures {
|
||||
Bool WritesViewportIndexOutput = false;
|
||||
Bool DeclaresMultisampledImage = false;
|
||||
Bool DeclaresWidenableImageFormat = false;
|
||||
};
|
||||
static SpirvGateFeatures ProbeSpirvGateFeatures(const Vector<Uint32>& binary);
|
||||
// Replaces an ARRAY vertex input with one input per element at consecutive
|
||||
@@ -241,15 +245,21 @@ namespace MobileGL {
|
||||
// declared format natively. See WidenImageFormatsPass for the table, for the nine
|
||||
// formats it deliberately does NOT widen, and for why the texture storage and the
|
||||
// glBindImageTexture argument have to move with it.
|
||||
// `onlyFormatsSpirvCrossRefusesToPrint` narrows it to the formats that have no
|
||||
// ESSL route even WITH GL_NV_image_formats, because SPIRV-Cross throws for them
|
||||
// rather than printing a token - which is the whole set a driver that advertises
|
||||
// the extension still needs. See WidenImageFormatsPass.
|
||||
static bool WidenImageFormatsForEssl(const Vector<Uint32>& inputBinary,
|
||||
Vector<uint32_t>& outputBinary,
|
||||
bool onlyFormatsSpirvCrossRefusesToPrint = false,
|
||||
bool enableSpirvValidation = false);
|
||||
// Whether the module declares a storage image WidenImageFormatsForEssl would
|
||||
// widen, so the ~every shader that declares none pays no optimizer run. Costs its
|
||||
// own module parse: the transpile path asks the same question through
|
||||
// ProbeSpirvGateFeatures instead, because this gate is armed on every real driver
|
||||
// and would otherwise put a BuildModule on every stage of every program.
|
||||
static bool DeclaresWidenableImageFormat(const Vector<Uint32>& binary);
|
||||
// widen, under the same mode the run would use. Costs its own module parse, so
|
||||
// the transpile path does NOT gate on this - it answers the question from the
|
||||
// frontend's uniform reflection instead, for the reason on SpirvGateFeatures.
|
||||
// Here for tests and for callers that already hold nothing but the binary.
|
||||
static bool DeclaresWidenableImageFormat(const Vector<Uint32>& binary,
|
||||
bool onlyFormatsSpirvCrossRefusesToPrint = false);
|
||||
// The core-ESSL GL internal format that carries `glInternalFormat` exactly, or 0
|
||||
// when it needs no widening or cannot be widened exactly. The single source of
|
||||
// truth for all three layers of the emulation: this one answers the shader, and
|
||||
|
||||
@@ -8,6 +8,10 @@
|
||||
|
||||
#include "WidenImageFormatsPass.h"
|
||||
|
||||
// For IsSpirvCrossEsslPrintableFormat: the two passes share one question about the emitter, and
|
||||
// the answer belongs where the rest of the image-format tables already are.
|
||||
#include "BakeImageFormatsPass.h"
|
||||
|
||||
#include "spirv.hpp"
|
||||
#include "source/opt/build_module.h"
|
||||
#include "source/opt/constants.h"
|
||||
@@ -211,12 +215,20 @@ namespace MobileGL {
|
||||
}
|
||||
}
|
||||
|
||||
Bool IsWidenableStorageImageType(const Instruction* type) {
|
||||
Bool IsWidenableStorageImageType(const Instruction* type,
|
||||
bool onlyFormatsSpirvCrossRefusesToPrint) {
|
||||
if (type == nullptr || type->opcode() != spv::Op::OpTypeImage) return false;
|
||||
if (type->GetSingleWordInOperand(kImageSampledOperand) != kSampledStorageImage) return false;
|
||||
const auto format =
|
||||
static_cast<spv::ImageFormat>(type->GetSingleWordInOperand(kImageFormatOperand));
|
||||
return static_cast<Bool>(WideningOfSpirvImageFormat(format));
|
||||
if (!WideningOfSpirvImageFormat(format)) return false;
|
||||
if (onlyFormatsSpirvCrossRefusesToPrint &&
|
||||
BakeImageFormatsPass::IsSpirvCrossEsslPrintableFormat(static_cast<Uint32>(format))) {
|
||||
// The driver can spell this one and the emitter will print it; widening it
|
||||
// would spend two to four times the texture memory to change nothing.
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
@@ -231,23 +243,25 @@ namespace MobileGL {
|
||||
return ChannelsOfSpirvImageFormat(SpirvImageFormatOfGL(glInternalFormat));
|
||||
}
|
||||
|
||||
bool WidenImageFormatsPass::DeclaresWidenableImageFormat(IRContext* context) {
|
||||
bool WidenImageFormatsPass::DeclaresWidenableImageFormat(
|
||||
IRContext* context, const bool onlyFormatsSpirvCrossRefusesToPrint) {
|
||||
if (context == nullptr) {
|
||||
return false;
|
||||
}
|
||||
for (const Instruction& type : context->module()->types_values()) {
|
||||
if (IsWidenableStorageImageType(&type)) {
|
||||
if (IsWidenableStorageImageType(&type, onlyFormatsSpirvCrossRefusesToPrint)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool WidenImageFormatsPass::DeclaresWidenableImageFormat(const Vector<Uint32>& binary) {
|
||||
bool WidenImageFormatsPass::DeclaresWidenableImageFormat(
|
||||
const Vector<Uint32>& binary, const bool onlyFormatsSpirvCrossRefusesToPrint) {
|
||||
std::unique_ptr<IRContext> context = spvtools::BuildModule(
|
||||
SPV_ENV_VULKAN_1_1, [](spv_message_level_t, const char*, const spv_position_t&, const char*) {},
|
||||
binary.data(), binary.size());
|
||||
return DeclaresWidenableImageFormat(context.get());
|
||||
return DeclaresWidenableImageFormat(context.get(), onlyFormatsSpirvCrossRefusesToPrint);
|
||||
}
|
||||
|
||||
spvtools::opt::Pass::Status WidenImageFormatsPass::Process() {
|
||||
@@ -258,7 +272,7 @@ namespace MobileGL {
|
||||
// byte-identical - which is every shader but a handful.
|
||||
std::vector<Instruction*> imageTypes;
|
||||
for (Instruction& type : irContext->types_values()) {
|
||||
if (IsWidenableStorageImageType(&type)) {
|
||||
if (IsWidenableStorageImageType(&type, m_onlyFormatsSpirvCrossRefusesToPrint)) {
|
||||
imageTypes.push_back(&type);
|
||||
}
|
||||
}
|
||||
@@ -539,8 +553,10 @@ namespace MobileGL {
|
||||
return Status::SuccessWithChange;
|
||||
}
|
||||
|
||||
spvtools::Optimizer::PassToken WidenImageFormatsPass::CreateWidenImageFormatsPass() {
|
||||
return spvtools::Optimizer::PassToken(spvtools::MakeUnique<WidenImageFormatsPass>());
|
||||
spvtools::Optimizer::PassToken WidenImageFormatsPass::CreateWidenImageFormatsPass(
|
||||
const bool onlyFormatsSpirvCrossRefusesToPrint) {
|
||||
return spvtools::Optimizer::PassToken(
|
||||
spvtools::MakeUnique<WidenImageFormatsPass>(onlyFormatsSpirvCrossRefusesToPrint));
|
||||
}
|
||||
} // namespace ShaderTranspiler
|
||||
} // namespace MG_Util
|
||||
|
||||
@@ -74,19 +74,39 @@ namespace MobileGL {
|
||||
// format from the same bind state, so the module must reach it unchanged.
|
||||
class WidenImageFormatsPass final : public spvtools::opt::Pass {
|
||||
public:
|
||||
// `onlyFormatsSpirvCrossRefusesToPrint` narrows the pass to the formats that have
|
||||
// no ESSL route even on a driver that DOES advertise GL_NV_image_formats.
|
||||
// SPIRV-Cross's is_desktop_only_format set - r8ui, rg16f, r16i and fifteen others -
|
||||
// makes it THROW for an ESSL target rather than print a token, and the throw takes
|
||||
// the stage with it whatever the driver could have accepted. Mesa is exactly that
|
||||
// case: it advertises the extension, so nothing else needs widening there, and
|
||||
// `layout(r8ui) uimage2D` still lost its whole program until this ran for it.
|
||||
//
|
||||
// Off, the pass widens every format in the table, which is what a driver without
|
||||
// the extension needs. The caller sets it from
|
||||
// g_GLESCapabilities.SupportsExtendedImageFormats, and the SAME rule decides
|
||||
// whether the ES texture storage and the glBindImageTexture argument widen
|
||||
// (TextureImpl::GetImageBindableStorageWidening) - all three have to agree or the
|
||||
// shader addresses a texel size the storage does not have.
|
||||
explicit WidenImageFormatsPass(bool onlyFormatsSpirvCrossRefusesToPrint = false)
|
||||
: m_onlyFormatsSpirvCrossRefusesToPrint(onlyFormatsSpirvCrossRefusesToPrint) {}
|
||||
|
||||
const char* name() const override { return "mobilegl-widen-image-formats"; }
|
||||
Status Process() override;
|
||||
|
||||
// Whether the module declares a storage image whose format this pass would widen,
|
||||
// i.e. whether running it could change anything. Answered from a single parse so
|
||||
// the caller can skip the optimizer run entirely - which is every shader but a
|
||||
// handful.
|
||||
static bool DeclaresWidenableImageFormat(const Vector<Uint32>& binary);
|
||||
// handful. `onlyFormatsSpirvCrossRefusesToPrint` must match what the run will use,
|
||||
// or the gate answers a question the pass is not being asked.
|
||||
static bool DeclaresWidenableImageFormat(const Vector<Uint32>& binary,
|
||||
bool onlyFormatsSpirvCrossRefusesToPrint = false);
|
||||
// The same question asked of a module the caller has ALREADY parsed, so a stage
|
||||
// that has to answer several gate questions pays one BuildModule rather than one
|
||||
// per gate - see ShaderCompiler::ProbeSpirvGateFeatures, and the ~10% it cost
|
||||
// compile-heavy CTS cases when two gates each parsed for themselves.
|
||||
static bool DeclaresWidenableImageFormat(spvtools::opt::IRContext* context);
|
||||
static bool DeclaresWidenableImageFormat(spvtools::opt::IRContext* context,
|
||||
bool onlyFormatsSpirvCrossRefusesToPrint = false);
|
||||
|
||||
// The core-ESSL GL internal format that carries `glInternalFormat` exactly, or 0
|
||||
// when the format needs no widening (it is core already) or cannot be widened
|
||||
@@ -99,7 +119,11 @@ namespace MobileGL {
|
||||
// forty image formats. The count the widened accesses are masked back to.
|
||||
static Uint ImageFormatChannelCount(Uint glInternalFormat);
|
||||
|
||||
static spvtools::Optimizer::PassToken CreateWidenImageFormatsPass();
|
||||
static spvtools::Optimizer::PassToken CreateWidenImageFormatsPass(
|
||||
bool onlyFormatsSpirvCrossRefusesToPrint = false);
|
||||
|
||||
private:
|
||||
bool m_onlyFormatsSpirvCrossRefusesToPrint = false;
|
||||
};
|
||||
} // namespace ShaderTranspiler
|
||||
} // namespace MG_Util
|
||||
|
||||
Reference in New Issue
Block a user