diff --git a/src/render/metal/SDL_render_metal.m b/src/render/metal/SDL_render_metal.m index 3bba129a67..e11fe1c2b3 100644 --- a/src/render/metal/SDL_render_metal.m +++ b/src/render/metal/SDL_render_metal.m @@ -79,15 +79,10 @@ static const size_t CONSTANTS_OFFSET_DECODE_BT2020_LIMITED = ALIGN_CONSTANTS(16, static const size_t CONSTANTS_OFFSET_DECODE_BT2020_FULL = ALIGN_CONSTANTS(16, CONSTANTS_OFFSET_DECODE_BT2020_LIMITED + sizeof(float) * 4 * 4); static const size_t CONSTANTS_LENGTH = CONSTANTS_OFFSET_DECODE_BT2020_FULL + sizeof(float) * 4 * 4; -// Sampler types -typedef enum -{ - SDL_METAL_SAMPLER_NEAREST_CLAMP, - SDL_METAL_SAMPLER_NEAREST_WRAP, - SDL_METAL_SAMPLER_LINEAR_CLAMP, - SDL_METAL_SAMPLER_LINEAR_WRAP, - SDL_NUM_METAL_SAMPLERS -} SDL_METAL_sampler_type; +#define RENDER_SAMPLER_HASHKEY(scale_mode, address_u, address_v) \ + (((scale_mode == SDL_SCALEMODE_NEAREST) << 0) | \ + ((address_u == SDL_TEXTURE_ADDRESS_WRAP) << 1) | \ + ((address_v == SDL_TEXTURE_ADDRESS_WRAP) << 2)) typedef enum SDL_MetalVertexFunction { @@ -139,7 +134,7 @@ typedef struct METAL_ShaderPipelines @property(nonatomic, retain) id mtlcmdencoder; @property(nonatomic, retain) id mtllibrary; @property(nonatomic, retain) id mtlbackbuffer; -@property(nonatomic, retain) NSMutableArray> *mtlsamplers; +@property(nonatomic, retain) NSMutableDictionary> *mtlsamplers; @property(nonatomic, retain) id mtlbufconstants; @property(nonatomic, retain) id mtlbufquadindices; @property(nonatomic, assign) SDL_MetalView mtlview; @@ -1295,6 +1290,9 @@ typedef struct __unsafe_unretained id vertex_buffer; size_t constants_offset; SDL_Texture *texture; + SDL_ScaleMode texture_scale_mode; + SDL_TextureAddressMode texture_address_mode_u; + SDL_TextureAddressMode texture_address_mode_v; bool cliprect_dirty; bool cliprect_enabled; SDL_Rect cliprect; @@ -1452,6 +1450,58 @@ static bool SetDrawState(SDL_Renderer *renderer, const SDL_RenderCommand *cmd, c return true; } +static id GetSampler(SDL3METAL_RenderData *data, SDL_ScaleMode scale_mode, SDL_TextureAddressMode address_u, SDL_TextureAddressMode address_v) +{ + NSNumber *key = [NSNumber numberWithInteger:RENDER_SAMPLER_HASHKEY(scale_mode, address_u, address_v)]; + id mtlsampler = data.mtlsamplers[key]; + if (mtlsampler == nil) { + MTLSamplerDescriptor *samplerdesc; + samplerdesc = [[MTLSamplerDescriptor alloc] init]; + switch (scale_mode) { + case SDL_SCALEMODE_NEAREST: + samplerdesc.minFilter = MTLSamplerMinMagFilterNearest; + samplerdesc.magFilter = MTLSamplerMinMagFilterNearest; + break; + case SDL_SCALEMODE_LINEAR: + samplerdesc.minFilter = MTLSamplerMinMagFilterLinear; + samplerdesc.magFilter = MTLSamplerMinMagFilterLinear; + break; + default: + SDL_SetError("Unknown scale mode: %d", scale_mode); + return nil; + } + switch (address_u) { + case SDL_TEXTURE_ADDRESS_CLAMP: + samplerdesc.sAddressMode = MTLSamplerAddressModeClampToEdge; + break; + case SDL_TEXTURE_ADDRESS_WRAP: + samplerdesc.sAddressMode = MTLSamplerAddressModeRepeat; + break; + default: + SDL_SetError("Unknown texture address mode: %d", address_u); + return nil; + } + switch (address_v) { + case SDL_TEXTURE_ADDRESS_CLAMP: + samplerdesc.tAddressMode = MTLSamplerAddressModeClampToEdge; + break; + case SDL_TEXTURE_ADDRESS_WRAP: + samplerdesc.tAddressMode = MTLSamplerAddressModeRepeat; + break; + default: + SDL_SetError("Unknown texture address mode: %d", address_v); + return nil; + } + mtlsampler = [data.mtldevice newSamplerStateWithDescriptor:samplerdesc]; + if (mtlsampler == nil) { + SDL_SetError("Couldn't create sampler"); + return nil; + } + data.mtlsamplers[key] = mtlsampler; + } + return mtlsampler; +} + static bool SetCopyState(SDL_Renderer *renderer, const SDL_RenderCommand *cmd, const size_t constants_offset, id mtlbufvertex, METAL_DrawStateCache *statecache) { @@ -1467,33 +1517,6 @@ static bool SetCopyState(SDL_Renderer *renderer, const SDL_RenderCommand *cmd, c } if (texture != statecache->texture) { - id mtlsampler; - - if (cmd->data.draw.texture_scale_mode == SDL_SCALEMODE_NEAREST) { - switch (cmd->data.draw.texture_address_mode) { - case SDL_TEXTURE_ADDRESS_CLAMP: - mtlsampler = data.mtlsamplers[SDL_METAL_SAMPLER_NEAREST_CLAMP]; - break; - case SDL_TEXTURE_ADDRESS_WRAP: - mtlsampler = data.mtlsamplers[SDL_METAL_SAMPLER_NEAREST_WRAP]; - break; - default: - return SDL_SetError("Unknown texture address mode: %d", cmd->data.draw.texture_address_mode); - } - } else { - switch (cmd->data.draw.texture_address_mode) { - case SDL_TEXTURE_ADDRESS_CLAMP: - mtlsampler = data.mtlsamplers[SDL_METAL_SAMPLER_LINEAR_CLAMP]; - break; - case SDL_TEXTURE_ADDRESS_WRAP: - mtlsampler = data.mtlsamplers[SDL_METAL_SAMPLER_LINEAR_WRAP]; - break; - default: - return SDL_SetError("Unknown texture address mode: %d", cmd->data.draw.texture_address_mode); - } - } - [data.mtlcmdencoder setFragmentSamplerState:mtlsampler atIndex:0]; - [data.mtlcmdencoder setFragmentTexture:texturedata.mtltexture atIndex:0]; #ifdef SDL_HAVE_YUV if (texturedata.yuv || texturedata.nv12) { @@ -1503,6 +1526,20 @@ static bool SetCopyState(SDL_Renderer *renderer, const SDL_RenderCommand *cmd, c #endif statecache->texture = texture; } + + if (cmd->data.draw.texture_scale_mode != statecache->texture_scale_mode || + cmd->data.draw.texture_address_mode != statecache->texture_address_mode_u || + cmd->data.draw.texture_address_mode != statecache->texture_address_mode_v) { + id mtlsampler = GetSampler(data, cmd->data.draw.texture_scale_mode, cmd->data.draw.texture_address_mode, cmd->data.draw.texture_address_mode); + if (mtlsampler == nil) { + return false; + } + [data.mtlcmdencoder setFragmentSamplerState:mtlsampler atIndex:0]; + + statecache->texture_scale_mode = cmd->data.draw.texture_scale_mode; + statecache->texture_address_mode_u = cmd->data.draw.texture_address_mode; + statecache->texture_address_mode_v = cmd->data.draw.texture_address_mode; + } return true; } @@ -1523,6 +1560,9 @@ static bool METAL_RunCommandQueue(SDL_Renderer *renderer, SDL_RenderCommand *cmd statecache.vertex_buffer = nil; statecache.constants_offset = CONSTANTS_OFFSET_INVALID; statecache.texture = NULL; + statecache.texture_scale_mode = SDL_SCALEMODE_INVALID; + statecache.texture_address_mode_u = SDL_TEXTURE_ADDRESS_INVALID; + statecache.texture_address_mode_v = SDL_TEXTURE_ADDRESS_INVALID; statecache.shader_constants_dirty = true; statecache.cliprect_dirty = true; statecache.viewport_dirty = true; @@ -1883,7 +1923,6 @@ static bool METAL_CreateRenderer(SDL_Renderer *renderer, SDL_Window *window, SDL int maxtexsize, quadcount = UINT16_MAX / 4; UInt16 *indexdata; size_t indicessize = sizeof(UInt16) * quadcount * 6; - MTLSamplerDescriptor *samplerdesc; id mtlcmdqueue; id mtllibrary; id mtlbufconstantstaging, mtlbufquadindicesstaging, mtlbufconstants, mtlbufquadindices; @@ -2043,27 +2082,7 @@ static bool METAL_CreateRenderer(SDL_Renderer *renderer, SDL_Window *window, SDL data.allpipelines = NULL; ChooseShaderPipelines(data, MTLPixelFormatBGRA8Unorm); - static struct - { - MTLSamplerMinMagFilter filter; - MTLSamplerAddressMode address; - } samplerParams[] = { - { MTLSamplerMinMagFilterNearest, MTLSamplerAddressModeClampToEdge }, - { MTLSamplerMinMagFilterNearest, MTLSamplerAddressModeRepeat }, - { MTLSamplerMinMagFilterLinear, MTLSamplerAddressModeClampToEdge }, - { MTLSamplerMinMagFilterLinear, MTLSamplerAddressModeRepeat }, - }; - SDL_COMPILE_TIME_ASSERT(samplerParams_SIZE, SDL_arraysize(samplerParams) == SDL_NUM_METAL_SAMPLERS); - - data.mtlsamplers = [[NSMutableArray> alloc] init]; - samplerdesc = [[MTLSamplerDescriptor alloc] init]; - for (int i = 0; i < SDL_arraysize(samplerParams); ++i) { - samplerdesc.minFilter = samplerParams[i].filter; - samplerdesc.magFilter = samplerParams[i].filter; - samplerdesc.sAddressMode = samplerParams[i].address; - samplerdesc.tAddressMode = samplerParams[i].address; - [data.mtlsamplers addObject:[data.mtldevice newSamplerStateWithDescriptor:samplerdesc]]; - } + data.mtlsamplers = [[NSMutableDictionary> alloc] init]; mtlbufconstantstaging = [data.mtldevice newBufferWithLength:CONSTANTS_LENGTH options:MTLResourceStorageModeShared];