Skip to content

Commit

Permalink
metal: Improved texture type support (#81)
Browse files Browse the repository at this point in the history
* metal: MSAA textures + array textures

* testgpu: Add --msaa option

* metal: Cube texture support
  • Loading branch information
TheSpydog authored and flibitijibibo committed Jul 16, 2024
1 parent 6ea7861 commit 8396b0f
Show file tree
Hide file tree
Showing 3 changed files with 211 additions and 73 deletions.
168 changes: 115 additions & 53 deletions src/gpu/metal/SDL_gpu_metal.m
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,13 @@ static void METAL_UnclaimWindow(
MTLVertexStepFunctionPerInstance,
};

static NSUInteger SDLToMetal_SampleCount[] = {
1, /* SDL_GPU_SAMPLECOUNT_1 */
2, /* SDL_GPU_SAMPLECOUNT_2 */
4, /* SDL_GPU_SAMPLECOUNT_4 */
8 /* SDL_GPU_SAMPLECOUNT_8 */
};

static SDL_GpuTextureFormat SwapchainCompositionToFormat[] = {
SDL_GPU_TEXTUREFORMAT_B8G8R8A8, /* SDR */
SDL_GPU_TEXTUREFORMAT_B8G8R8A8_SRGB, /* SDR_LINEAR */
Expand Down Expand Up @@ -324,6 +331,7 @@ static MTLColorWriteMask SDLToMetal_ColorWriteMask(
typedef struct MetalTexture
{
id<MTLTexture> handle;
id<MTLTexture> msaaHandle;
SDL_AtomicInt referenceCount;
} MetalTexture;

Expand Down Expand Up @@ -719,16 +727,14 @@ static MetalLibraryFunction METAL_INTERNAL_CompileShader(
if (format == SDL_GPU_SHADERFORMAT_MSL) {
library = [renderer->device
newLibraryWithSource:@((const char *)code)
options:nil /* FIXME: Do we need any compile options? */
options:nil
error:&error];
} else if (format == SDL_GPU_SHADERFORMAT_METALLIB) {
data = dispatch_data_create(
code,
codeSize,
dispatch_get_global_queue(0, 0),
^{
} /* FIXME: is this right? */
);
^{ /* do nothing */ });
library = [renderer->device newLibraryWithData:data error:&error];
} else {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Incompatible shader format for Metal");
Expand Down Expand Up @@ -962,7 +968,9 @@ static void METAL_ReleaseGraphicsPipeline(
pipelineDescriptor.colorAttachments[i].destinationAlphaBlendFactor = SDLToMetal_BlendFactor[blendState->dstAlphaBlendFactor];
}

/* FIXME: Multisample */
/* Multisample */

pipelineDescriptor.rasterSampleCount = SDLToMetal_SampleCount[pipelineCreateInfo->multisampleState.multisampleCount];

/* Depth Stencil */

Expand Down Expand Up @@ -1248,27 +1256,22 @@ static void METAL_PopDebugGroup(
{
MTLTextureDescriptor *textureDescriptor = [MTLTextureDescriptor new];
id<MTLTexture> texture;
id<MTLTexture> msaaTexture = NULL;
MetalTexture *metalTexture;

/* FIXME: MSAA? */
if (textureCreateInfo->depth > 1) {
textureDescriptor.textureType = MTLTextureType3D;
} else if (textureCreateInfo->isCube) {
textureDescriptor.textureType = MTLTextureTypeCube;
if (textureCreateInfo->depth <= 1) {
if (textureCreateInfo->isCube) {
textureDescriptor.textureType = MTLTextureTypeCube;
} else if (textureCreateInfo->layerCount > 1) {
textureDescriptor.textureType = MTLTextureType2DArray;
} else {
textureDescriptor.textureType = MTLTextureType2D;
}
} else {
textureDescriptor.textureType = MTLTextureType2D;
textureDescriptor.textureType = MTLTextureType3D;
}

textureDescriptor.pixelFormat = SDLToMetal_SurfaceFormat[textureCreateInfo->format];
textureDescriptor.width = textureCreateInfo->width;
textureDescriptor.height = textureCreateInfo->height;
textureDescriptor.depth = textureCreateInfo->depth;
textureDescriptor.mipmapLevelCount = textureCreateInfo->levelCount;
textureDescriptor.sampleCount = 1; /* FIXME */
textureDescriptor.arrayLength = 1; /* FIXME: Is this used outside of cubes? */
textureDescriptor.resourceOptions = MTLResourceCPUCacheModeDefaultCache | MTLResourceStorageModePrivate | MTLResourceHazardTrackingModeDefault;
textureDescriptor.allowGPUOptimizedContents = true;

/* This format isn't natively supported so let's swizzle! */
if (textureCreateInfo->format == SDL_GPU_TEXTUREFORMAT_B4G4R4A4) {
textureDescriptor.swizzle = MTLTextureSwizzleChannelsMake(
Expand All @@ -1278,26 +1281,51 @@ static void METAL_PopDebugGroup(
MTLTextureSwizzleAlpha);
}

textureDescriptor.width = textureCreateInfo->width;
textureDescriptor.height = textureCreateInfo->height;
textureDescriptor.depth = textureCreateInfo->depth;
textureDescriptor.mipmapLevelCount = textureCreateInfo->levelCount;
textureDescriptor.sampleCount = 1;
textureDescriptor.arrayLength = (textureCreateInfo->isCube) ? 1 : textureCreateInfo->layerCount; /* FIXME: Cube arrays? */
textureDescriptor.storageMode = MTLStorageModePrivate;

textureDescriptor.usage = 0;
if (textureCreateInfo->usageFlags & (SDL_GPU_TEXTUREUSAGE_COLOR_TARGET_BIT | SDL_GPU_TEXTUREUSAGE_DEPTH_STENCIL_TARGET_BIT)) {
if (textureCreateInfo->usageFlags & (SDL_GPU_TEXTUREUSAGE_COLOR_TARGET_BIT |
SDL_GPU_TEXTUREUSAGE_DEPTH_STENCIL_TARGET_BIT)) {
textureDescriptor.usage |= MTLTextureUsageRenderTarget;
}
if (textureCreateInfo->usageFlags & SDL_GPU_TEXTUREUSAGE_SAMPLER_BIT) {
if (textureCreateInfo->usageFlags & (SDL_GPU_TEXTUREUSAGE_SAMPLER_BIT |
SDL_GPU_TEXTUREUSAGE_GRAPHICS_STORAGE_READ_BIT |
SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_READ_BIT)) {
textureDescriptor.usage |= MTLTextureUsageShaderRead;
}
if (textureCreateInfo->usageFlags & SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_WRITE_BIT) {
textureDescriptor.usage |= MTLTextureUsageShaderRead | MTLTextureUsageShaderWrite;
textureDescriptor.usage |= MTLTextureUsageShaderWrite;
}
/* FIXME: Other usages! */

texture = [renderer->device newTextureWithDescriptor:textureDescriptor];
if (texture == NULL) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to create MTLTexture!");
return NULL;
}

/* Create the MSAA texture, if needed */
if (textureCreateInfo->sampleCount > SDL_GPU_SAMPLECOUNT_1 && textureDescriptor.textureType == MTLTextureType2D) {
textureDescriptor.textureType = MTLTextureType2DMultisample;
textureDescriptor.sampleCount = SDLToMetal_SampleCount[textureCreateInfo->sampleCount];
textureDescriptor.usage = MTLTextureUsageRenderTarget;

msaaTexture = [renderer->device newTextureWithDescriptor:textureDescriptor];
if (msaaTexture == NULL) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to create MSAA MTLTexture!");
return NULL;
}
}

metalTexture = (MetalTexture *)SDL_malloc(sizeof(MetalTexture));
metalTexture->handle = texture;
metalTexture->msaaHandle = msaaTexture;
SDL_AtomicSet(&metalTexture->referenceCount, 0);
return metalTexture;
}

Expand Down Expand Up @@ -2079,53 +2107,75 @@ static void METAL_BeginRenderPass(
SDL_GpuDepthStencilAttachmentInfo *depthStencilAttachmentInfo)
{
MetalCommandBuffer *metalCommandBuffer = (MetalCommandBuffer *)commandBuffer;
MetalRenderer *renderer = metalCommandBuffer->renderer;
MTLRenderPassDescriptor *passDescriptor = [MTLRenderPassDescriptor renderPassDescriptor];
SDL_GpuColorAttachmentInfo *attachmentInfo;
MetalTexture *texture;
Uint32 vpWidth = UINT_MAX;
Uint32 vpHeight = UINT_MAX;
MTLViewport viewport;
MTLScissorRect scissorRect;

for (Uint32 i = 0; i < colorAttachmentCount; i += 1) {
attachmentInfo = &colorAttachmentInfos[i];
texture = ((MetalTextureContainer *)attachmentInfo->textureSlice.texture)->activeTexture;
MetalTextureContainer *container = (MetalTextureContainer *)colorAttachmentInfos[i].textureSlice.texture;
MetalTexture *texture = METAL_INTERNAL_PrepareTextureForWrite(
renderer,
container,
colorAttachmentInfos[i].cycle);

/* FIXME: cycle! */
passDescriptor.colorAttachments[i].texture = texture->handle;
passDescriptor.colorAttachments[i].level = attachmentInfo->textureSlice.mipLevel;
passDescriptor.colorAttachments[i].slice = attachmentInfo->textureSlice.layer;
if (texture->msaaHandle) {
passDescriptor.colorAttachments[i].texture = texture->msaaHandle;
passDescriptor.colorAttachments[i].resolveTexture = texture->handle;
} else {
passDescriptor.colorAttachments[i].texture = texture->handle;
}
passDescriptor.colorAttachments[i].level = colorAttachmentInfos[i].textureSlice.mipLevel;
passDescriptor.colorAttachments[i].slice = colorAttachmentInfos[i].textureSlice.layer;
passDescriptor.colorAttachments[i].clearColor = MTLClearColorMake(
attachmentInfo->clearColor.r,
attachmentInfo->clearColor.g,
attachmentInfo->clearColor.b,
attachmentInfo->clearColor.a);
passDescriptor.colorAttachments[i].loadAction = SDLToMetal_LoadOp[attachmentInfo->loadOp];
passDescriptor.colorAttachments[i].storeAction = SDLToMetal_StoreOp(attachmentInfo->storeOp, 0);
/* FIXME: Resolve texture! Also affects ^! */
colorAttachmentInfos[i].clearColor.r,
colorAttachmentInfos[i].clearColor.g,
colorAttachmentInfos[i].clearColor.b,
colorAttachmentInfos[i].clearColor.a);
passDescriptor.colorAttachments[i].loadAction = SDLToMetal_LoadOp[colorAttachmentInfos[i].loadOp];
passDescriptor.colorAttachments[i].storeAction = SDLToMetal_StoreOp(
colorAttachmentInfos[i].storeOp,
texture->msaaHandle ? 1 : 0);

METAL_INTERNAL_TrackTexture(metalCommandBuffer, texture);
}

if (depthStencilAttachmentInfo != NULL) {
MetalTextureContainer *container = (MetalTextureContainer *)depthStencilAttachmentInfo->textureSlice.texture;
texture = container->activeTexture;
MetalTexture *texture = METAL_INTERNAL_PrepareTextureForWrite(
renderer,
container,
depthStencilAttachmentInfo->cycle);

/* FIXME: cycle! */
passDescriptor.depthAttachment.texture = texture->handle;
if (texture->msaaHandle) {
passDescriptor.depthAttachment.texture = texture->msaaHandle;
passDescriptor.depthAttachment.resolveTexture = texture->handle;
} else {
passDescriptor.depthAttachment.texture = texture->handle;
}
passDescriptor.depthAttachment.level = depthStencilAttachmentInfo->textureSlice.mipLevel;
passDescriptor.depthAttachment.slice = depthStencilAttachmentInfo->textureSlice.layer;
passDescriptor.depthAttachment.loadAction = SDLToMetal_LoadOp[depthStencilAttachmentInfo->loadOp];
passDescriptor.depthAttachment.storeAction = SDLToMetal_StoreOp(depthStencilAttachmentInfo->storeOp, 0);
passDescriptor.depthAttachment.storeAction = SDLToMetal_StoreOp(
depthStencilAttachmentInfo->storeOp,
texture->msaaHandle ? 1 : 0);
passDescriptor.depthAttachment.clearDepth = depthStencilAttachmentInfo->depthStencilClearValue.depth;

if (IsStencilFormat(container->createInfo.format)) {
/* FIXME: cycle! */
passDescriptor.stencilAttachment.texture = texture->handle;
if (texture->msaaHandle) {
passDescriptor.stencilAttachment.texture = texture->msaaHandle;
passDescriptor.stencilAttachment.resolveTexture = texture->handle;
} else {
passDescriptor.stencilAttachment.texture = texture->handle;
}
passDescriptor.stencilAttachment.level = depthStencilAttachmentInfo->textureSlice.mipLevel;
passDescriptor.stencilAttachment.slice = depthStencilAttachmentInfo->textureSlice.layer;
passDescriptor.stencilAttachment.loadAction = SDLToMetal_LoadOp[depthStencilAttachmentInfo->loadOp];
passDescriptor.stencilAttachment.storeAction = SDLToMetal_StoreOp(depthStencilAttachmentInfo->storeOp, 0);
passDescriptor.stencilAttachment.storeAction = SDLToMetal_StoreOp(
depthStencilAttachmentInfo->storeOp,
texture->msaaHandle ? 1 : 0);
passDescriptor.stencilAttachment.clearStencil = depthStencilAttachmentInfo->depthStencilClearValue.stencil;
}

Expand All @@ -2149,7 +2199,19 @@ static void METAL_BeginRenderPass(
}
}

/* FIXME: check depth/stencil attachment size too */
if (depthStencilAttachmentInfo != NULL) {
MetalTextureContainer *container = (MetalTextureContainer *)depthStencilAttachmentInfo->textureSlice.texture;
Uint32 w = container->createInfo.width >> depthStencilAttachmentInfo->textureSlice.mipLevel;
Uint32 h = container->createInfo.height >> depthStencilAttachmentInfo->textureSlice.mipLevel;

if (w < vpWidth) {
vpWidth = w;
}

if (h < vpHeight) {
vpHeight = h;
}
}

/* Set default viewport and scissor state */
viewport.originX = 0;
Expand Down Expand Up @@ -2949,8 +3011,9 @@ static void METAL_Blit(
METAL_BindGraphicsPipeline(commandBuffer, pipeline);

textureSamplerBinding.texture = source->textureSlice.texture;
textureSamplerBinding.sampler =
filterMode == SDL_GPU_FILTER_NEAREST ? renderer->blitNearestSampler : renderer->blitLinearSampler;
textureSamplerBinding.sampler = (filterMode == SDL_GPU_FILTER_NEAREST)
? renderer->blitNearestSampler
: renderer->blitLinearSampler;

METAL_BindFragmentSamplers(
commandBuffer,
Expand Down Expand Up @@ -3344,7 +3407,7 @@ static Uint8 METAL_INTERNAL_CreateSwapchain(
#endif
windowData->layer.pixelFormat = SDLToMetal_SurfaceFormat[SwapchainCompositionToFormat[swapchainComposition]];
#ifndef SDL_PLATFORM_TVOS
windowData->layer.wantsExtendedDynamicRangeContent = (swapchainComposition != SDL_GPU_SWAPCHAINCOMPOSITION_SDR); /* FIXME: Metadata? */
windowData->layer.wantsExtendedDynamicRangeContent = (swapchainComposition != SDL_GPU_SWAPCHAINCOMPOSITION_SDR);
#endif

colorspace = CGColorSpaceCreateWithName(SwapchainCompositionToColorSpace[swapchainComposition]);
Expand All @@ -3363,8 +3426,7 @@ static Uint8 METAL_INTERNAL_CreateSwapchain(
windowData->textureContainer.createInfo.levelCount = 1;
windowData->textureContainer.createInfo.depth = 1;
windowData->textureContainer.createInfo.isCube = 0;
windowData->textureContainer.createInfo.usageFlags =
SDL_GPU_TEXTUREUSAGE_COLOR_TARGET_BIT | SDL_GPU_TEXTUREUSAGE_SAMPLER_BIT | SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_WRITE_BIT; /* FIXME: Other bits? */
windowData->textureContainer.createInfo.usageFlags = SDL_GPU_TEXTUREUSAGE_COLOR_TARGET_BIT | SDL_GPU_TEXTUREUSAGE_SAMPLER_BIT;

drawableSize = windowData->layer.drawableSize;
windowData->textureContainer.createInfo.width = (Uint32)drawableSize.width;
Expand Down Expand Up @@ -3555,7 +3617,7 @@ static SDL_bool METAL_SetSwapchainParameters(
#endif
windowData->layer.pixelFormat = SDLToMetal_SurfaceFormat[SwapchainCompositionToFormat[swapchainComposition]];
#ifndef SDL_PLATFORM_TVOS
windowData->layer.wantsExtendedDynamicRangeContent = (swapchainComposition != SDL_GPU_SWAPCHAINCOMPOSITION_SDR); /* FIXME: Metadata? */
windowData->layer.wantsExtendedDynamicRangeContent = (swapchainComposition != SDL_GPU_SWAPCHAINCOMPOSITION_SDR);
#endif

colorspace = CGColorSpaceCreateWithName(SwapchainCompositionToColorSpace[swapchainComposition]);
Expand Down
5 changes: 5 additions & 0 deletions src/gpu/vulkan/SDL_gpu_vulkan.c
Original file line number Diff line number Diff line change
Expand Up @@ -9004,6 +9004,11 @@ static void VULKAN_Blit(
VulkanTextureContainer *sourceTextureContainer = (VulkanTextureContainer *)source->textureSlice.texture;
VkImageBlit region;

if ((sourceTextureContainer->activeTextureHandle->vulkanTexture->usageFlags & SDL_GPU_TEXTUREUSAGE_SAMPLER_BIT) == 0) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Blit source texture must be created with SAMPLER bit!");
return;
}

VulkanTextureSlice *srcTextureSlice = VULKAN_INTERNAL_FetchTextureSlice(
sourceTextureContainer->activeTextureHandle->vulkanTexture,
source->textureSlice.layer,
Expand Down
Loading

0 comments on commit 8396b0f

Please sign in to comment.