Commit 5e13757b by Le Hoang Quyen Committed by Commit Bot

Metal: Fix array of structs containing array of samplers bug.

Previously ProgramMtl could try to bind fixed slots to samplers based on layout (set=..., binding=...). However, GLSL layout model is different from Metal slots assignment. For example, The following is valid layout in GLSL: - array samplers A[2] is bound to index 0. - array samplers B[2] is bound to index 1. It is invalid to do so in Metal, since A occupies 2 slots, thus samplers B[2] must be bound to slots starting from 2. New binding method: let spirv-cross auto assigns the texture slots and retrieve them after compilation. Incomplete textures moved to ContextMtl using IncompleteTextureSet. New test added: GLSLTest.ArrayOfStructContainingArrayOfSamplers. Bug: angleproject:2634 Change-Id: Ib0edaaf8b20512e1272c37c1d4b16a88a5b35e75 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2193193 Commit-Queue: Jamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarJonah Ryan-Davis <jonahr@google.com>
parent 4b2a9cbc
...@@ -240,6 +240,10 @@ class ContextMtl : public ContextImpl, public mtl::Context ...@@ -240,6 +240,10 @@ class ContextMtl : public ContextImpl, public mtl::Context
const mtl::VertexFormat &getVertexFormat(angle::FormatID angleFormatId, const mtl::VertexFormat &getVertexFormat(angle::FormatID angleFormatId,
bool tightlyPacked) const; bool tightlyPacked) const;
angle::Result getIncompleteTexture(const gl::Context *context,
gl::TextureType type,
gl::Texture **textureOut);
// Recommended to call these methods to end encoding instead of invoking the encoder's // Recommended to call these methods to end encoding instead of invoking the encoder's
// endEncoding() directly. // endEncoding() directly.
void endEncoding(mtl::RenderCommandEncoder *encoder); void endEncoding(mtl::RenderCommandEncoder *encoder);
...@@ -282,6 +286,7 @@ class ContextMtl : public ContextImpl, public mtl::Context ...@@ -282,6 +286,7 @@ class ContextMtl : public ContextImpl, public mtl::Context
private: private:
void ensureCommandBufferValid(); void ensureCommandBufferValid();
angle::Result ensureIncompleteTexturesCreated(const gl::Context *context);
angle::Result setupDraw(const gl::Context *context, angle::Result setupDraw(const gl::Context *context,
gl::PrimitiveMode mode, gl::PrimitiveMode mode,
GLint firstVertex, GLint firstVertex,
...@@ -455,6 +460,9 @@ class ContextMtl : public ContextImpl, public mtl::Context ...@@ -455,6 +460,9 @@ class ContextMtl : public ContextImpl, public mtl::Context
DriverUniforms mDriverUniforms; DriverUniforms mDriverUniforms;
DefaultAttribute mDefaultAttributes[mtl::kMaxVertexAttribs]; DefaultAttribute mDefaultAttributes[mtl::kMaxVertexAttribs];
IncompleteTextureSet mIncompleteTextures;
bool mIncompleteTexturesInitialized = false;
}; };
} // namespace rx } // namespace rx
......
...@@ -121,6 +121,28 @@ void ContextMtl::onDestroy(const gl::Context *context) ...@@ -121,6 +121,28 @@ void ContextMtl::onDestroy(const gl::Context *context)
{ {
mTriFanIndexBuffer.destroy(this); mTriFanIndexBuffer.destroy(this);
mLineLoopIndexBuffer.destroy(this); mLineLoopIndexBuffer.destroy(this);
mIncompleteTextures.onDestroy(context);
mIncompleteTexturesInitialized = false;
}
angle::Result ContextMtl::ensureIncompleteTexturesCreated(const gl::Context *context)
{
if (ANGLE_LIKELY(mIncompleteTexturesInitialized))
{
return angle::Result::Continue;
}
constexpr gl::TextureType supportedTextureTypes[] = {gl::TextureType::_2D,
gl::TextureType::CubeMap};
for (gl::TextureType texType : supportedTextureTypes)
{
gl::Texture *texture;
ANGLE_UNUSED_VARIABLE(texture);
ANGLE_TRY(mIncompleteTextures.getIncompleteTexture(context, texType, nullptr, &texture));
}
mIncompleteTexturesInitialized = true;
return angle::Result::Continue;
} }
// Flush and finish. // Flush and finish.
...@@ -519,6 +541,9 @@ angle::Result ContextMtl::syncState(const gl::Context *context, ...@@ -519,6 +541,9 @@ angle::Result ContextMtl::syncState(const gl::Context *context,
{ {
const gl::State &glState = context->getState(); const gl::State &glState = context->getState();
// Initialize incomplete texture set.
ANGLE_TRY(ensureIncompleteTexturesCreated(context));
for (size_t dirtyBit : dirtyBits) for (size_t dirtyBit : dirtyBits)
{ {
switch (dirtyBit) switch (dirtyBit)
...@@ -1023,6 +1048,13 @@ const mtl::VertexFormat &ContextMtl::getVertexFormat(angle::FormatID angleFormat ...@@ -1023,6 +1048,13 @@ const mtl::VertexFormat &ContextMtl::getVertexFormat(angle::FormatID angleFormat
return getDisplay()->getVertexFormat(angleFormatId, tightlyPacked); return getDisplay()->getVertexFormat(angleFormatId, tightlyPacked);
} }
angle::Result ContextMtl::getIncompleteTexture(const gl::Context *context,
gl::TextureType type,
gl::Texture **textureOut)
{
return mIncompleteTextures.getIncompleteTexture(context, type, nullptr, textureOut);
}
void ContextMtl::endEncoding(mtl::RenderCommandEncoder *encoder) void ContextMtl::endEncoding(mtl::RenderCommandEncoder *encoder)
{ {
encoder->endEncoding(); encoder->endEncoding();
......
...@@ -120,8 +120,6 @@ class DisplayMtl : public DisplayImpl ...@@ -120,8 +120,6 @@ class DisplayMtl : public DisplayImpl
return mStateCache.getSamplerState(getMetalDevice(), desc); return mStateCache.getSamplerState(getMetalDevice(), desc);
} }
const mtl::TextureRef &getNullTexture(const gl::Context *context, gl::TextureType type);
const mtl::Format &getPixelFormat(angle::FormatID angleFormatId) const const mtl::Format &getPixelFormat(angle::FormatID angleFormatId) const
{ {
return mFormatTable.getPixelFormat(angleFormatId); return mFormatTable.getPixelFormat(angleFormatId);
...@@ -158,8 +156,6 @@ class DisplayMtl : public DisplayImpl ...@@ -158,8 +156,6 @@ class DisplayMtl : public DisplayImpl
// Built-in Shaders // Built-in Shaders
mtl::AutoObjCPtr<id<MTLLibrary>> mDefaultShaders = nil; mtl::AutoObjCPtr<id<MTLLibrary>> mDefaultShaders = nil;
angle::PackedEnumMap<gl::TextureType, mtl::TextureRef> mNullTextures;
mutable bool mCapsInitialized; mutable bool mCapsInitialized;
mutable gl::TextureCapsMap mNativeTextureCaps; mutable gl::TextureCapsMap mNativeTextureCaps;
mutable gl::Extensions mNativeExtensions; mutable gl::Extensions mNativeExtensions;
......
...@@ -95,10 +95,6 @@ angle::Result DisplayMtl::initializeImpl(egl::Display *display) ...@@ -95,10 +95,6 @@ angle::Result DisplayMtl::initializeImpl(egl::Display *display)
void DisplayMtl::terminate() void DisplayMtl::terminate()
{ {
for (mtl::TextureRef &nullTex : mNullTextures)
{
nullTex.reset();
}
mUtils.onDestroy(); mUtils.onDestroy();
mCmdQueue.reset(); mCmdQueue.reset();
mDefaultShaders = nil; mDefaultShaders = nil;
...@@ -380,45 +376,6 @@ const gl::Extensions &DisplayMtl::getNativeExtensions() const ...@@ -380,45 +376,6 @@ const gl::Extensions &DisplayMtl::getNativeExtensions() const
return mNativeExtensions; return mNativeExtensions;
} }
const mtl::TextureRef &DisplayMtl::getNullTexture(const gl::Context *context, gl::TextureType type)
{
// TODO(hqle): Use rx::IncompleteTextureSet.
ContextMtl *contextMtl = mtl::GetImpl(context);
if (!mNullTextures[type])
{
// initialize content with zeros
MTLRegion region = MTLRegionMake2D(0, 0, 1, 1);
const uint8_t zeroPixel[4] = {0, 0, 0, 255};
const auto &rgbaFormat = getPixelFormat(angle::FormatID::R8G8B8A8_UNORM);
switch (type)
{
case gl::TextureType::_2D:
(void)(mtl::Texture::Make2DTexture(contextMtl, rgbaFormat, 1, 1, 1, false, false,
&mNullTextures[type]));
mNullTextures[type]->replaceRegion(contextMtl, region, 0, 0, zeroPixel,
sizeof(zeroPixel));
break;
case gl::TextureType::CubeMap:
(void)(mtl::Texture::MakeCubeTexture(contextMtl, rgbaFormat, 1, 1, false, false,
&mNullTextures[type]));
for (int f = 0; f < 6; ++f)
{
mNullTextures[type]->replaceRegion(contextMtl, region, 0, f, zeroPixel,
sizeof(zeroPixel));
}
break;
default:
UNREACHABLE();
// NOTE(hqle): Support more texture types.
}
ASSERT(mNullTextures[type]);
}
return mNullTextures[type];
}
void DisplayMtl::ensureCapsInitialized() const void DisplayMtl::ensureCapsInitialized() const
{ {
if (mCapsInitialized) if (mCapsInitialized)
...@@ -521,7 +478,7 @@ void DisplayMtl::ensureCapsInitialized() const ...@@ -521,7 +478,7 @@ void DisplayMtl::ensureCapsInitialized() const
// Note that we currently implement textures as combined image+samplers, so the limit is // Note that we currently implement textures as combined image+samplers, so the limit is
// the minimum of supported samplers and sampled images. // the minimum of supported samplers and sampled images.
mNativeCaps.maxCombinedTextureImageUnits = mtl::kMaxShaderSamplers; mNativeCaps.maxCombinedTextureImageUnits = mtl::kMaxGLSamplerBindings;
mNativeCaps.maxShaderTextureImageUnits[gl::ShaderType::Fragment] = mtl::kMaxShaderSamplers; mNativeCaps.maxShaderTextureImageUnits[gl::ShaderType::Fragment] = mtl::kMaxShaderSamplers;
mNativeCaps.maxShaderTextureImageUnits[gl::ShaderType::Vertex] = mtl::kMaxShaderSamplers; mNativeCaps.maxShaderTextureImageUnits[gl::ShaderType::Vertex] = mtl::kMaxShaderSamplers;
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#include "libANGLE/renderer/ProgramImpl.h" #include "libANGLE/renderer/ProgramImpl.h"
#include "libANGLE/renderer/glslang_wrapper_utils.h" #include "libANGLE/renderer/glslang_wrapper_utils.h"
#include "libANGLE/renderer/metal/mtl_command_buffer.h" #include "libANGLE/renderer/metal/mtl_command_buffer.h"
#include "libANGLE/renderer/metal/mtl_glslang_utils.h"
#include "libANGLE/renderer/metal/mtl_resources.h" #include "libANGLE/renderer/metal/mtl_resources.h"
#include "libANGLE/renderer/metal/mtl_state_cache.h" #include "libANGLE/renderer/metal/mtl_state_cache.h"
...@@ -130,10 +131,6 @@ class ProgramMtl : public ProgramImpl ...@@ -130,10 +131,6 @@ class ProgramMtl : public ProgramImpl
angle::Result linkImpl(const gl::Context *glContext, angle::Result linkImpl(const gl::Context *glContext,
const gl::ProgramLinkedResources &resources, const gl::ProgramLinkedResources &resources,
gl::InfoLog &infoLog); gl::InfoLog &infoLog);
angle::Result convertToMsl(const gl::Context *glContext,
gl::ShaderType shaderType,
gl::InfoLog &infoLog,
std::vector<uint32_t> *sprivCode);
angle::Result createMslShader(const gl::Context *glContext, angle::Result createMslShader(const gl::Context *glContext,
gl::ShaderType shaderType, gl::ShaderType shaderType,
...@@ -158,6 +155,10 @@ class ProgramMtl : public ProgramImpl ...@@ -158,6 +155,10 @@ class ProgramMtl : public ProgramImpl
gl::ShaderBitSet mSamplerBindingsDirty; gl::ShaderBitSet mSamplerBindingsDirty;
gl::ShaderMap<DefaultUniformBlock> mDefaultUniformBlocks; gl::ShaderMap<DefaultUniformBlock> mDefaultUniformBlocks;
gl::ShaderMap<std::string> mTranslatedMslShader;
gl::ShaderMap<mtl::TranslatedShaderInfo> mMslShaderTranslateInfo;
mtl::RenderPipelineCache mMetalRenderPipelineCache; mtl::RenderPipelineCache mMetalRenderPipelineCache;
}; };
......
...@@ -112,6 +112,9 @@ constexpr uint32_t kUniformBufferSettingOffsetMinAlignment = 4; ...@@ -112,6 +112,9 @@ constexpr uint32_t kUniformBufferSettingOffsetMinAlignment = 4;
#endif #endif
constexpr uint32_t kIndexBufferOffsetAlignment = 4; constexpr uint32_t kIndexBufferOffsetAlignment = 4;
// Front end binding limits
constexpr uint32_t kMaxGLSamplerBindings = 2 * kMaxShaderSamplers;
// Binding index start for vertex data buffers: // Binding index start for vertex data buffers:
constexpr uint32_t kVboBindingIndexStart = 0; constexpr uint32_t kVboBindingIndexStart = 0;
......
...@@ -19,6 +19,19 @@ namespace rx ...@@ -19,6 +19,19 @@ namespace rx
{ {
namespace mtl namespace mtl
{ {
struct SamplerBinding
{
uint32_t textureBinding = 0;
uint32_t samplerBinding = 0;
};
struct TranslatedShaderInfo
{
std::array<SamplerBinding, kMaxGLSamplerBindings> actualSamplerBindings;
// NOTE(hqle): UBO, XFB bindings.
};
void GlslangGetShaderSource(const gl::ProgramState &programState, void GlslangGetShaderSource(const gl::ProgramState &programState,
const gl::ProgramLinkedResources &resources, const gl::ProgramLinkedResources &resources,
gl::ShaderMap<std::string> *shaderSourcesOut, gl::ShaderMap<std::string> *shaderSourcesOut,
...@@ -30,6 +43,14 @@ angle::Result GlslangGetShaderSpirvCode(ErrorHandler *context, ...@@ -30,6 +43,14 @@ angle::Result GlslangGetShaderSpirvCode(ErrorHandler *context,
const gl::ShaderMap<std::string> &shaderSources, const gl::ShaderMap<std::string> &shaderSources,
const ShaderMapInterfaceVariableInfoMap &variableInfoMap, const ShaderMapInterfaceVariableInfoMap &variableInfoMap,
gl::ShaderMap<std::vector<uint32_t>> *shaderCodeOut); gl::ShaderMap<std::vector<uint32_t>> *shaderCodeOut);
// Translate from SPIR-V code to Metal shader source code.
angle::Result SpirvCodeToMsl(Context *context,
const gl::ProgramState &programState,
gl::ShaderMap<std::vector<uint32_t>> *sprivShaderCode,
gl::ShaderMap<TranslatedShaderInfo> *mslShaderInfoOut,
gl::ShaderMap<std::string> *mslCodeOut);
} // namespace mtl } // namespace mtl
} // namespace rx } // namespace rx
......
...@@ -2276,6 +2276,47 @@ void main() ...@@ -2276,6 +2276,47 @@ void main()
EXPECT_PIXEL_RECT_EQ(0, 0, getWindowWidth(), getWindowHeight(), GLColor::red); EXPECT_PIXEL_RECT_EQ(0, 0, getWindowWidth(), getWindowHeight(), GLColor::red);
} }
// Test that array of structs containing array of samplers work as expected.
TEST_P(GLSLTest, ArrayOfStructContainingArrayOfSamplers)
{
constexpr char kFS[] =
"precision mediump float;\n"
"struct Data { mediump sampler2D data[2]; };\n"
"uniform Data test[2];\n"
"void main() {\n"
" gl_FragColor = vec4(texture2D(test[1].data[1], vec2(0.0, 0.0)).r,\n"
" texture2D(test[1].data[0], vec2(0.0, 0.0)).r,\n"
" texture2D(test[0].data[1], vec2(0.0, 0.0)).r,\n"
" texture2D(test[0].data[0], vec2(0.0, 0.0)).r);\n"
"}\n";
ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), kFS);
glUseProgram(program.get());
GLTexture textures[4];
GLColor expected = MakeGLColor(32, 64, 96, 255);
GLubyte data[8] = {}; // 4 bytes of padding, so that texture can be initialized with 4 bytes
memcpy(data, expected.data(), sizeof(expected));
for (int i = 0; i < 4; i++)
{
int outerIdx = i % 2;
int innerIdx = i / 2;
glActiveTexture(GL_TEXTURE0 + i);
glBindTexture(GL_TEXTURE_2D, textures[i]);
// Each element provides two components.
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, data + i);
std::stringstream uniformName;
uniformName << "test[" << innerIdx << "].data[" << outerIdx << "]";
// Then send it as a uniform
GLint uniformLocation = glGetUniformLocation(program.get(), uniformName.str().c_str());
// The uniform should be active.
EXPECT_NE(uniformLocation, -1);
glUniform1i(uniformLocation, 3 - i);
}
drawQuad(program.get(), essl1_shaders::PositionAttrib(), 0.5f);
EXPECT_PIXEL_COLOR_EQ(0, 0, expected);
}
// Test that two constructors which have vec4 and mat2 parameters get disambiguated (issue in // Test that two constructors which have vec4 and mat2 parameters get disambiguated (issue in
// HLSL). // HLSL).
TEST_P(GLSLTest_ES3, AmbiguousConstructorCall2x2) TEST_P(GLSLTest_ES3, AmbiguousConstructorCall2x2)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment