Improve shader module and empty texture managment

This commit is contained in:
halx99 2023-07-28 11:46:34 +08:00
parent d882208b8b
commit 0f642ddb2e
14 changed files with 120 additions and 214 deletions

View File

@ -325,23 +325,6 @@ Sprite::~Sprite()
* Texture methods
*/
/*
* This array is the data of a white image with 2 by 2 dimension.
* It's used for creating a default texture when sprite's texture is set to nullptr.
* Supposing codes as follows:
*
* auto sp = new Sprite();
* sp->init(); // Texture was set to nullptr, in order to make opacity and color to work correctly, we need to create
* a 2x2 white texture.
*
* The test is in "TestCpp/SpriteTest/Sprite without texture".
*/
static unsigned char cc_2x2_white_image[] = {
// RGBA8888
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
#define AX_2x2_WHITE_IMAGE_KEY "/cc_2x2_white_image"
// MARK: texture
void Sprite::setTexture(std::string_view filename)
{
@ -394,18 +377,7 @@ void Sprite::setTexture(Texture2D* texture)
if (texture == nullptr)
{
// Gets the texture by key firstly.
texture = _director->getTextureCache()->getTextureForKey(AX_2x2_WHITE_IMAGE_KEY);
// If texture wasn't in cache, create it from RAW data.
if (texture == nullptr)
{
Image* image = new Image();
bool AX_UNUSED isOK = image->initWithRawData(cc_2x2_white_image, sizeof(cc_2x2_white_image), 2, 2, 8);
AXASSERT(isOK, "The 2x2 empty texture was created unsuccessfully.");
texture = _director->getTextureCache()->addImage(image, AX_2x2_WHITE_IMAGE_KEY);
AX_SAFE_RELEASE(image);
}
texture = _director->getTextureCache()->getWhiteTexture();
}
bool needsUpdatePS =

View File

@ -90,25 +90,6 @@ void Mesh::resetLightUniformValues()
_spotLightUniformRangeInverseValues.assign(maxSpotLight, 0.0f);
}
// Generate a dummy texture when the texture file is missing
static Texture2D* getDummyTexture()
{
auto texture = Director::getInstance()->getTextureCache()->getTextureForKey("/dummyTexture");
if (!texture)
{
#ifdef NDEBUG
unsigned char data[] = {0, 0, 0, 0}; // 1*1 transparent picture
#else
unsigned char data[] = {255, 0, 0, 255}; // 1*1 red picture
#endif
Image* image = new Image();
image->initWithRawData(data, sizeof(data), 1, 1, sizeof(unsigned char));
texture = Director::getInstance()->getTextureCache()->addImage(image, "/dummyTexture");
image->release();
}
return texture;
}
Mesh::Mesh()
: _skin(nullptr)
, _visible(true)
@ -282,7 +263,7 @@ void Mesh::setTexture(Texture2D* tex, NTextureData::Usage usage, bool cacheFileN
// it doesn't matter if the material is already set or not
// This functionality is added for compatibility issues
if (tex == nullptr)
tex = getDummyTexture();
tex = Director::getInstance()->getTextureCache()->getDummyTexture();
AX_SAFE_RETAIN(tex);
AX_SAFE_RELEASE(_textures[usage]);

View File

@ -108,7 +108,7 @@ public:
void updateMVPUniform(const Mat4& modelView);
void setUniformTexture(uint32_t slot, backend::TextureBackend*); // u_tex0
void setUniformNormTexture(uint32_t slot, backend::TextureBackend*); // u_tex0
void setUniformNormTexture(uint32_t slot, backend::TextureBackend*); // u_normalTex
void setUniformColor(const void*, size_t); // ucolor
void setUniformMatrixPalette(const void*, size_t); // u_matrixPalette

View File

@ -384,6 +384,42 @@ void TextureCache::addImageAsyncCallBack(float /*dt*/)
}
}
Texture2D* TextureCache::getWhiteTexture()
{
constexpr std::string_view key = "/white-texture"sv;
// Gets the texture by key firstly.
auto texture = this->getTextureForKey(key);
if (texture) return texture;
// If texture wasn't in cache, create it from RAW data.
unsigned char texls[] = {
// RGBA8888
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
Image image;
bool AX_UNUSED isOK = image.initWithRawData(texls, sizeof(texls), 2, 2, 8);
AXASSERT(isOK, "The 2x2 empty texture was created unsuccessfully.");
return this->addImage(&image, key);
}
Texture2D* TextureCache::getDummyTexture()
{
constexpr std::string_view key = "/dummy-texture"sv;
// Gets the texture by key firstly.
auto texture = this->getTextureForKey(key);
if (texture) return texture;
// If texture wasn't in cache, create it from RAW data.
#ifdef NDEBUG
unsigned char texls[] = {0, 0, 0, 0}; // 1*1 transparent picture
#else
unsigned char texls[] = {255, 0, 0, 255}; // 1*1 red picture
#endif
Image image;
bool AX_UNUSED isOK = image.initWithRawData(texls, sizeof(texls), 1, 1, sizeof(unsigned char));
return this->addImage(&image, key);
}
Texture2D* TextureCache::addImage(std::string_view path)
{
return addImage(path, Texture2D::getDefaultAlphaPixelFormat());

View File

@ -84,6 +84,12 @@ public:
* @lua NA
*/
virtual std::string getDescription() const;
/** Gets a 2x2 white texture */
Texture2D* getWhiteTexture();
/** Gets 1x1 dummy texture with alpha=0 */
Texture2D* getDummyTexture();
// Dictionary* snapshotTextures();

View File

@ -52,11 +52,11 @@ enum class BufferType : uint32_t
UNIFORM = UNIFORM_BUFFER
};
enum class ShaderStage : uint32_t
enum class ShaderStage
{
UNKNOWN = -1,
VERTEX,
FRAGMENT,
VERTEX_AND_FRAGMENT
FRAGMENT
};
enum class VertexFormat : uint32_t

View File

@ -236,14 +236,9 @@ void ProgramState::setUniform(const backend::UniformLocation& uniformLocation, c
setVertexUniform(uniformLocation.location[0], data, size, uniformLocation.location[1]);
break;
case backend::ShaderStage::FRAGMENT:
setFragmentUniform(uniformLocation.location[1], data, size);
break;
case backend::ShaderStage::VERTEX_AND_FRAGMENT:
setVertexUniform(uniformLocation.location[0], data, size, uniformLocation.location[1]);
setFragmentUniform(uniformLocation.location[1], data, size);
break;
default:
setFragmentUniform(uniformLocation.location[0], data, size, uniformLocation.location[1]);
break;
default:;
}
}
@ -256,14 +251,13 @@ void ProgramState::setVertexUniform(int location, const void* data, std::size_t
memcpy(_vertexUniformBuffer + location + offset, data, size);
}
void ProgramState::setFragmentUniform(int location, const void* data, std::size_t size)
void ProgramState::setFragmentUniform(int location, const void* data, std::size_t size, std::size_t offset)
{
if (location < 0)
return;
// float3 etc in Metal has both sizeof and alignment same as float4, need convert to correct laytout
#ifdef AX_USE_METAL
memcpy(_fragmentUniformBuffer + location, data, size);
memcpy(_fragmentUniformBuffer + location + offset, data, size);
#else
assert(false);
#endif
@ -345,14 +339,9 @@ void ProgramState::setTexture(const backend::UniformLocation& uniformLocation,
setTexture(uniformLocation.location[0], slot, index, texture, _vertexTextureInfos);
break;
case backend::ShaderStage::FRAGMENT:
setTexture(uniformLocation.location[1], slot, index, texture, _fragmentTextureInfos);
break;
case backend::ShaderStage::VERTEX_AND_FRAGMENT:
setTexture(uniformLocation.location[0], slot, index, texture, _vertexTextureInfos);
setTexture(uniformLocation.location[1], slot, index, texture, _fragmentTextureInfos);
break;
default:
setTexture(uniformLocation.location[0], slot, index, texture, _fragmentTextureInfos);
break;
default:;
}
}
@ -366,14 +355,9 @@ void ProgramState::setTextureArray(const backend::UniformLocation& uniformLocati
setTextureArray(uniformLocation.location[0], std::move(slots), std::move(textures), _vertexTextureInfos);
break;
case backend::ShaderStage::FRAGMENT:
setTextureArray(uniformLocation.location[1], std::move(slots), std::move(textures), _fragmentTextureInfos);
break;
case backend::ShaderStage::VERTEX_AND_FRAGMENT:
setTextureArray(uniformLocation.location[0], std::move(slots), std::move(textures), _vertexTextureInfos);
setTextureArray(uniformLocation.location[1], std::move(slots), std::move(textures), _fragmentTextureInfos);
break;
default:
setTextureArray(uniformLocation.location[0], std::move(slots), std::move(textures), _fragmentTextureInfos);
break;
default:;
}
}

View File

@ -342,7 +342,7 @@ protected:
* @param data Specifies the new values to be used for the specified uniform variable.
* @param size Specifies the uniform data size.
*/
void setFragmentUniform(int location, const void* data, std::size_t size);
void setFragmentUniform(int location, const void* data, std::size_t size, std::size_t offset);
/**
* Set texture.

View File

@ -81,24 +81,22 @@ struct UniformLocation
UniformLocation() {
location[0] = -1;
location[1] = -1;
shaderStage = ShaderStage::UNKNOWN;
}
UniformLocation(int loc, int offset)
UniformLocation(int loc, int offset, ShaderStage stage = ShaderStage::VERTEX)
{
location[0] = loc;
location[1] = offset;
shaderStage = stage;
}
/**
* in metal, those two locations represent to vertex and fragment location.
* in opengl, location[0] represent the location, and location[1] represent location offset in uniform block.
* both opengl and metal, location[0] represent the location, and location[1] represent location offset in uniform block.
*/
int location[2];
ShaderStage shaderStage = ShaderStage::VERTEX;
ShaderStage shaderStage;
operator bool()
{
if (shaderStage == ShaderStage::VERTEX_AND_FRAGMENT)
return location[0] >= 0 && location[1] >= 0;
else
return location[int(shaderStage)] >= 0;
return shaderStage != ShaderStage::UNKNOWN;
}
void reset() { location[0] = location[1] = -1; }
bool operator==(const UniformLocation& other) const;

View File

@ -62,49 +62,19 @@ int ProgramMTL::getAttributeLocation(std::string_view name) const
UniformLocation ProgramMTL::getUniformLocation(backend::Uniform name) const
{
UniformLocation uniformLocation;
auto vsLocation = _vertexShader->getUniformLocation(name);
auto fsLocation = _fragmentShader->getUniformLocation(name);
if (vsLocation != -1 && fsLocation != -1)
{
uniformLocation.shaderStage = ShaderStage::VERTEX_AND_FRAGMENT;
uniformLocation.location[0] = vsLocation;
uniformLocation.location[1] = fsLocation;
}
else if (vsLocation != -1)
{
uniformLocation.shaderStage = ShaderStage::VERTEX;
uniformLocation.location[0] = vsLocation;
}
else
{
uniformLocation.shaderStage = ShaderStage::FRAGMENT;
uniformLocation.location[1] = fsLocation;
}
return uniformLocation;
uniformLocation = _vertexShader->getUniformLocation(name);
if (uniformLocation.location[0] != -1)
return uniformLocation;
return _fragmentShader->getUniformLocation(name);
}
UniformLocation ProgramMTL::getUniformLocation(std::string_view uniform) const
{
UniformLocation uniformLocation;
auto vsLocation = _vertexShader->getUniformLocation(uniform);
auto fsLocation = _fragmentShader->getUniformLocation(uniform);
if (vsLocation != -1 && fsLocation != -1)
{
uniformLocation.shaderStage = ShaderStage::VERTEX_AND_FRAGMENT;
uniformLocation.location[0] = vsLocation;
uniformLocation.location[1] = fsLocation;
}
else if (vsLocation != -1)
{
uniformLocation.shaderStage = ShaderStage::VERTEX;
uniformLocation.location[0] = vsLocation;
}
else
{
uniformLocation.shaderStage = ShaderStage::FRAGMENT;
uniformLocation.location[1] = fsLocation;
}
return uniformLocation;
uniformLocation = _vertexShader->getUniformLocation(uniform);
if (uniformLocation.location[0] != -1)
return uniformLocation;
return _fragmentShader->getUniformLocation(uniform);
}
int ProgramMTL::getMaxVertexLocation() const

View File

@ -84,14 +84,14 @@ public:
* @param name Specifies the engine built-in uniform enum name.
* @return The uniform location.
*/
int getUniformLocation(Uniform name) const;
UniformLocation getUniformLocation(Uniform name) const;
/**
* Get uniform location by name.
* @param uniform Specifies the uniform name.
* @return The uniform location.
*/
int getUniformLocation(std::string_view name) const;
UniformLocation getUniformLocation(std::string_view name) const;
/**
* Get attribute location by engine built-in attribute enum name.
@ -117,17 +117,18 @@ private:
void parseAttibute(SLCReflectContext* context);
void parseUniform(SLCReflectContext* context);
void parseTexture(SLCReflectContext* context);
void setBuiltinUniformLocation();
void setBuiltinAttributeLocation();
void setBuiltinLocations();
id<MTLFunction> _mtlFunction = nil;
hlookup::string_map<UniformInfo> _activeUniformInfos;
hlookup::string_map<AttributeBindInfo> _attributeInfo;
int _maxLocation = -1;
int _uniformLocation[UNIFORM_MAX];
int _attributeLocation[ATTRIBUTE_MAX];
int _maxLocation = -1;
UniformLocation _uniformLocation[UNIFORM_MAX]; // the builtin uniform locations
std::size_t _uniformBufferSize = 0;
};

View File

@ -179,8 +179,7 @@ ShaderModuleMTL::ShaderModuleMTL(id<MTLDevice> mtlDevice, ShaderStage stage, std
assert(false);
}
setBuiltinUniformLocation();
setBuiltinAttributeLocation();
setBuiltinLocations();
[library release];
}
@ -230,11 +229,11 @@ void ShaderModuleMTL::parseUniform(SLCReflectContext* context)
auto array_size = ibs->read<uint16_t>();
uniform.count = array_size;
uniform.location = i;
uniform.location = ub_binding;
uniform.size = size_bytes;
uniform.bufferOffset = offset;
uniform.type = format;
_activeUniformInfos[name] = uniform;
_activeUniformInfos[name] = uniform;
if (_maxLocation < i)
_maxLocation = (i + 1);
@ -256,71 +255,63 @@ void ShaderModuleMTL::parseTexture(SLCReflectContext* context)
ibs->advance(sizeof(sgs_refl_texture) - offsetof(sgs_refl_texture, image_dim));
UniformInfo uniform;
uniform.location = binding;
uniform.location = binding;
uniform.bufferOffset = -1;
_activeUniformInfos[name] = uniform;
}
}
int ShaderModuleMTL::getUniformLocation(Uniform name) const
UniformLocation ShaderModuleMTL::getUniformLocation(Uniform name) const
{
return _uniformLocation[name];
}
int ShaderModuleMTL::getUniformLocation(std::string_view name) const
UniformLocation ShaderModuleMTL::getUniformLocation(std::string_view name) const
{
auto iter = _activeUniformInfos.find(name);
if (iter != _activeUniformInfos.end())
{
return iter->second.location;
return UniformLocation{static_cast<int>(iter->second.location),
static_cast<int>(iter->second.bufferOffset), _stage};
}
else
return -1;
return UniformLocation{};
}
void ShaderModuleMTL::setBuiltinUniformLocation()
void ShaderModuleMTL::setBuiltinLocations()
{
std::fill(_uniformLocation, _uniformLocation + UNIFORM_MAX, -1);
/// u_mvpMatrix
auto iter = _activeUniformInfos.find(UNIFORM_NAME_MVP_MATRIX);
if (iter != _activeUniformInfos.end())
{
_uniformLocation[Uniform::MVP_MATRIX] = iter->second.location;
}
/*--- Builtin Attribs ---*/
/// u_textColor
iter = _activeUniformInfos.find(UNIFORM_NAME_TEXT_COLOR);
if (iter != _activeUniformInfos.end())
{
_uniformLocation[Uniform::TEXT_COLOR] = iter->second.location;
}
/// a_position
_attributeLocation[Attribute::POSITION] = getAttributeLocation(ATTRIBUTE_NAME_POSITION);
/// u_effectColor
iter = _activeUniformInfos.find(UNIFORM_NAME_EFFECT_COLOR);
if (iter != _activeUniformInfos.end())
{
_uniformLocation[Uniform::EFFECT_COLOR] = iter->second.location;
}
/// a_color
_attributeLocation[Attribute::COLOR] = getAttributeLocation(ATTRIBUTE_NAME_COLOR);
/// u_effectType
iter = _activeUniformInfos.find(UNIFORM_NAME_EFFECT_TYPE);
if (iter != _activeUniformInfos.end())
{
_uniformLocation[Uniform::EFFECT_TYPE] = iter->second.location;
}
/// a_texCoord
_attributeLocation[Attribute::TEXCOORD] = getAttributeLocation(ATTRIBUTE_NAME_TEXCOORD);
// a_normal
_attributeLocation[Attribute::NORMAL] = getAttributeLocation(ATTRIBUTE_NAME_NORMAL);
/*--- Builtin Uniforms ---*/
/// u_MVPMatrix
_uniformLocation[Uniform::MVP_MATRIX] = getUniformLocation(UNIFORM_NAME_MVP_MATRIX);
/// u_tex0
iter = _activeUniformInfos.find(UNIFORM_NAME_TEXTURE);
if (iter != _activeUniformInfos.end())
{
_uniformLocation[Uniform::TEXTURE] = iter->second.location;
}
_uniformLocation[Uniform::TEXTURE] = getUniformLocation(UNIFORM_NAME_TEXTURE);
/// u_tex1
iter = _activeUniformInfos.find(UNIFORM_NAME_TEXTURE1);
if (iter != _activeUniformInfos.end())
{
_uniformLocation[Uniform::TEXTURE1] = iter->second.location;
}
_uniformLocation[Uniform::TEXTURE1] = getUniformLocation(UNIFORM_NAME_TEXTURE1);
/// u_textColor
_uniformLocation[Uniform::TEXT_COLOR] = getUniformLocation(UNIFORM_NAME_TEXT_COLOR);
/// u_effectColor
_uniformLocation[Uniform::EFFECT_COLOR] = getUniformLocation(UNIFORM_NAME_EFFECT_COLOR);
/// u_effectType
_uniformLocation[Uniform::EFFECT_TYPE] = getUniformLocation(UNIFORM_NAME_EFFECT_TYPE);
}
int ShaderModuleMTL::getAttributeLocation(Attribute name) const
@ -337,36 +328,4 @@ int ShaderModuleMTL::getAttributeLocation(std::string_view name)
return -1;
}
void ShaderModuleMTL::setBuiltinAttributeLocation()
{
std::fill(_attributeLocation, _attributeLocation + ATTRIBUTE_MAX, -1);
/// a_position
auto iter = _attributeInfo.find(ATTRIBUTE_NAME_POSITION);
if (iter != _attributeInfo.end())
{
_attributeLocation[Attribute::POSITION] = iter->second.location;
}
/// a_color
iter = _attributeInfo.find(ATTRIBUTE_NAME_COLOR);
if (iter != _attributeInfo.end())
{
_attributeLocation[Attribute::COLOR] = iter->second.location;
}
/// a_texCoord
iter = _attributeInfo.find(ATTRIBUTE_NAME_TEXCOORD);
if (iter != _attributeInfo.end())
{
_attributeLocation[Attribute::TEXCOORD] = iter->second.location;
}
/// a_normal
iter = _attributeInfo.find(ATTRIBUTE_NAME_NORMAL);
if (iter != _attributeInfo.end())
{
_attributeLocation[Attribute::NORMAL] = iter->second.location;
}
}
NS_AX_BACKEND_END

View File

@ -19,11 +19,11 @@ layout(location = SPOTLIGHT_NORM) in vec3 v_spotLightDirection[MAX_SPOT_LIGHT_NU
layout(location = NORMAL) in vec3 v_normal;
#endif
#ifdef USE_NORMAL_MAPPING
layout(binding = 0) uniform sampler2D u_normalTex;
#endif
layout(binding = 0) uniform sampler2D u_tex0;
layout(binding = 1) uniform sampler2D u_tex0;
#ifdef USE_NORMAL_MAPPING
layout(binding = 1) uniform sampler2D u_normalTex;
#endif
layout(std140) uniform fs_ub {
vec3 u_DirLightSourceColor[MAX_DIRECTIONAL_LIGHT_NUM];

View File

@ -4,7 +4,6 @@ layout(location = TEXCOORD0) in vec2 a_texCoord;
layout(location = TEXCOORD0) out vec2 v_texCoord;
layout(std140, binding = 0) uniform vs_ub {
float offset;
mat4 u_MVPMatrix;
};