mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-11 21:58:31 +09:00
[Feat] (DirectGLES): support rectangle textures where the emulation is exact
ES has no rectangle target and no rectangle sampler, so DirectGLES declared
GL_TEXTURE_RECTANGLE unsupported outright: the texture was never synced or bound,
and SPIRV-Cross refused the shader ("Rectangle textures are not supported on
OpenGL ES") which left the whole program unlinkable.
A rectangle texture is a single-level, clamped 2D texture whose only real
difference is that its lookups take non-normalized coordinates. Where every use
takes *integer* texel coordinates - texelFetch, textureSize - that difference
does not exist at all, and the two are the same thing. So:
- A new SPIR-V pass rewrites Dim::Rect image types to Dim::2D before
transpiling, and restates the rectangle capabilities as Shader. It declines
any module containing a normalized-coordinate lookup rather than emitting
something subtly wrong; SPIRV-Cross then rejects that module exactly as
before, so nothing that used to work changes and nothing new renders wrongly.
- The target maps to GL_TEXTURE_2D for storage, uploads and binding, alongside
the existing 1D and 1D-array emulation.
Fixes KHR-GL31.texture_size_promotion.functional outright, which takes GL31 to
100% conformance. GL32/GL33 advance past their rectangle cases to a separate
GL_RGB16 multisample issue. No regressions across texture_swizzle, shaders30,
texture_lod_*, framebuffer_blit, packed_depth_stencil, transform_feedback,
clip_distance or draw_buffers; DirectVulkan re-verified unaffected.
This commit is contained in:
@@ -363,6 +363,86 @@ namespace MobileGL {
|
||||
return optimizer.Run(inputBinary.data(), inputBinary.size(), &outputBinary, options);
|
||||
}
|
||||
|
||||
bool ShaderCompiler::LowerRectImagesForEssl(const Vector<Uint32>& inputBinary,
|
||||
Vector<uint32_t>& outputBinary) {
|
||||
constexpr SizeT kSpirvHeaderWordCount = 5;
|
||||
// OpTypeImage: [0] opcode/wordcount, [1] result id, [2] sampled type, [3] Dim, ...
|
||||
constexpr SizeT kTypeImageDimWordIndex = 3;
|
||||
constexpr SizeT kTypeImageMinWordCount = 9;
|
||||
outputBinary.clear();
|
||||
if (inputBinary.size() < kSpirvHeaderWordCount || inputBinary[0] != spv::MagicNumber) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Vector<SizeT> rectDimWordOffsets;
|
||||
Vector<SizeT> rectCapabilityWordOffsets;
|
||||
Bool hasNormalizedCoordinateLookup = false;
|
||||
for (SizeT offset = kSpirvHeaderWordCount; offset < inputBinary.size();) {
|
||||
const Uint32 instructionWord = inputBinary[offset];
|
||||
const SizeT wordCount = instructionWord >> 16u;
|
||||
const auto opcode = static_cast<spv::Op>(instructionWord & 0xffffu);
|
||||
if (wordCount == 0 || offset + wordCount > inputBinary.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (opcode == spv::Op::OpTypeImage && wordCount >= kTypeImageMinWordCount) {
|
||||
if (static_cast<spv::Dim>(inputBinary[offset + kTypeImageDimWordIndex]) == spv::Dim::Rect) {
|
||||
rectDimWordOffsets.push_back(offset + kTypeImageDimWordIndex);
|
||||
}
|
||||
} else if (opcode == spv::Op::OpCapability && wordCount >= 2) {
|
||||
const auto capability = static_cast<spv::Capability>(inputBinary[offset + 1]);
|
||||
if (capability == spv::Capability::SampledRect ||
|
||||
capability == spv::Capability::ImageRect) {
|
||||
rectCapabilityWordOffsets.push_back(offset + 1);
|
||||
}
|
||||
} else {
|
||||
switch (opcode) {
|
||||
// Everything that takes normalized coordinates. Tracing each one back to
|
||||
// its image type would let a module mix a normalized 2D lookup with a
|
||||
// rectangle fetch, but the extra reach is not worth the risk of getting
|
||||
// the trace wrong: decline the whole module instead.
|
||||
case spv::Op::OpImageSampleImplicitLod:
|
||||
case spv::Op::OpImageSampleExplicitLod:
|
||||
case spv::Op::OpImageSampleDrefImplicitLod:
|
||||
case spv::Op::OpImageSampleDrefExplicitLod:
|
||||
case spv::Op::OpImageSampleProjImplicitLod:
|
||||
case spv::Op::OpImageSampleProjExplicitLod:
|
||||
case spv::Op::OpImageSampleProjDrefImplicitLod:
|
||||
case spv::Op::OpImageSampleProjDrefExplicitLod:
|
||||
case spv::Op::OpImageGather:
|
||||
case spv::Op::OpImageDrefGather:
|
||||
case spv::Op::OpImageSparseSampleImplicitLod:
|
||||
case spv::Op::OpImageSparseSampleExplicitLod:
|
||||
case spv::Op::OpImageSparseSampleDrefImplicitLod:
|
||||
case spv::Op::OpImageSparseSampleDrefExplicitLod:
|
||||
case spv::Op::OpImageSparseGather:
|
||||
case spv::Op::OpImageSparseDrefGather:
|
||||
hasNormalizedCoordinateLookup = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
offset += wordCount;
|
||||
}
|
||||
|
||||
if (rectDimWordOffsets.empty() || hasNormalizedCoordinateLookup) {
|
||||
return false;
|
||||
}
|
||||
|
||||
outputBinary.assign(inputBinary.begin(), inputBinary.end());
|
||||
for (const SizeT dimWordOffset : rectDimWordOffsets) {
|
||||
outputBinary[dimWordOffset] = static_cast<Uint32>(spv::Dim::Dim2D);
|
||||
}
|
||||
// The rectangle capabilities describe types that no longer exist. Shader is always
|
||||
// declared by a graphics module, so restating it keeps the word count intact
|
||||
// without leaving a capability SPIRV-Cross would key off.
|
||||
for (const SizeT capabilityWordOffset : rectCapabilityWordOffsets) {
|
||||
outputBinary[capabilityWordOffset] = static_cast<Uint32>(spv::Capability::Shader);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ShaderCompiler::RebaseInstanceIndexForVulkan(const Vector<Uint32>& inputBinary,
|
||||
Vector<uint32_t>& outputBinary) {
|
||||
using namespace spvtools;
|
||||
|
||||
@@ -43,6 +43,16 @@ namespace MobileGL {
|
||||
// devices lacking GL_NV_shader_noperspective_interpolation. See EmulateNoPerspectivePass.
|
||||
static bool EmulateNoPerspectiveForEssl(const Vector<Uint32>& inputBinary,
|
||||
Vector<uint32_t>& outputBinary);
|
||||
// Rewrites rectangle images (Dim::Rect) to plain 2D so SPIRV-Cross can emit ESSL
|
||||
// for them at all - it refuses outright ("Rectangle textures are not supported on
|
||||
// OpenGL ES"), which left the whole program unlinkable. Only valid while every use
|
||||
// of the image takes integer texel coordinates (texelFetch / textureSize), where a
|
||||
// rectangle target and a 2D target are indistinguishable; a normalized-coordinate
|
||||
// lookup would also need its coordinates divided by the texture size, so the pass
|
||||
// declines those modules instead of emitting something subtly wrong. Returns false
|
||||
// when it changed nothing or cannot safely convert. DirectGLES only.
|
||||
static bool LowerRectImagesForEssl(const Vector<Uint32>& inputBinary,
|
||||
Vector<uint32_t>& outputBinary);
|
||||
// Rebases loads of the InstanceIndex builtin to (InstanceIndex - BaseInstance) so
|
||||
// shaders see GL's zero-based gl_InstanceID. Vertex shaders only; DirectVulkan
|
||||
// backend only (glslang's relaxed mode aliases gl_InstanceID to gl_InstanceIndex,
|
||||
|
||||
Reference in New Issue
Block a user