Skip to content
Snippets Groups Projects
Commit 15ee235f authored by Feifan He's avatar Feifan He
Browse files

tests/shader_runner: Implement draw operation of metal runner.

parent 8c8e620a
Branches
Tags
1 merge request!1268ci test
......@@ -18,10 +18,12 @@
#include "config.h"
#import <Metal/Metal.h>
#define COBJMACROS
#define VKD3D_TEST_NO_DEFS
/* Avoid conflicts with the Objective C BOOL definition. */
#define BOOL VKD3D_BOOLEAN
#include "shader_runner.h"
#include "vkd3d_d3dcommon.h"
#undef BOOL
static const MTLResourceOptions DEFAULT_BUFFER_RESOURCE_OPTION = MTLResourceCPUCacheModeDefaultCache
......@@ -95,6 +97,81 @@ static unsigned int get_metal_format_texel_size(DXGI_FORMAT format)
}
};
static MTLVertexFormat get_metal_attribute_format(DXGI_FORMAT format)
{
switch (format)
{
case DXGI_FORMAT_R32G32B32A32_FLOAT:
return MTLVertexFormatFloat4;
case DXGI_FORMAT_R32G32B32A32_UINT:
return MTLVertexFormatUInt4;
case DXGI_FORMAT_R32G32B32A32_SINT:
return MTLVertexFormatInt4;
case DXGI_FORMAT_R32G32_FLOAT:
return MTLVertexFormatFloat2;
case DXGI_FORMAT_R32G32_UINT:
return MTLVertexFormatUInt2;
case DXGI_FORMAT_R32G32_SINT:
return MTLVertexFormatInt2;
case DXGI_FORMAT_R32_FLOAT:
return MTLVertexFormatFloat;
case DXGI_FORMAT_R32_UINT:
return MTLVertexFormatUInt;
case DXGI_FORMAT_R32_SINT:
return MTLVertexFormatInt;
default:
return MTLVertexFormatInvalid;
}
};
static bool get_metal_primitive_type(D3D_PRIMITIVE_TOPOLOGY primitive_topology, MTLPrimitiveType *primitive_type)
{
switch (primitive_topology)
{
case D3D_PRIMITIVE_TOPOLOGY_POINTLIST:
*primitive_type = MTLPrimitiveTypePoint;
break;
case D3D_PRIMITIVE_TOPOLOGY_LINELIST:
*primitive_type = MTLPrimitiveTypeLine;
break;
case D3D_PRIMITIVE_TOPOLOGY_LINESTRIP:
*primitive_type = MTLPrimitiveTypeLineStrip;
break;
case D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST:
*primitive_type = MTLPrimitiveTypeTriangle;
break;
case D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP:
*primitive_type = MTLPrimitiveTypeTriangleStrip;
break;
default:
return false;
}
return true;
}
static MTLCompareFunction get_metal_compare_function(D3D12_COMPARISON_FUNC func)
{
switch (func)
{
case VKD3D_SHADER_COMPARISON_FUNC_NEVER:
return MTLCompareFunctionNever;
case VKD3D_SHADER_COMPARISON_FUNC_LESS:
return MTLCompareFunctionLess;
case VKD3D_SHADER_COMPARISON_FUNC_EQUAL:
return MTLCompareFunctionEqual;
case VKD3D_SHADER_COMPARISON_FUNC_LESS_EQUAL:
return MTLCompareFunctionLessEqual;
case VKD3D_SHADER_COMPARISON_FUNC_GREATER:
return MTLCompareFunctionGreater;
case VKD3D_SHADER_COMPARISON_FUNC_NOT_EQUAL:
return MTLCompareFunctionNotEqual;
case VKD3D_SHADER_COMPARISON_FUNC_GREATER_EQUAL:
return MTLCompareFunctionGreaterEqual;
case VKD3D_SHADER_COMPARISON_FUNC_ALWAYS:
return MTLCompareFunctionAlways;
}
}
static struct metal_resource *metal_resource(struct resource *r)
{
return CONTAINING_RECORD(r, struct metal_resource, r);
......@@ -107,6 +184,9 @@ struct metal_runner
id<MTLDevice> device;
id<MTLCommandQueue> queue;
ID3D10Blob *d3d_blobs[SHADER_TYPE_COUNT];
struct vkd3d_shader_scan_signature_info signatures[SHADER_TYPE_COUNT];
};
static struct metal_runner *metal_runner(struct shader_runner *r)
......@@ -164,7 +244,7 @@ static void init_resource_2d(struct metal_runner *runner, struct metal_resource
if (![device supportsTextureSampleCount:params->desc.sample_count])
{
trace("Format #%x with sample count %u is not supported; skipping.\n", params->desc.format,
skip("Format #%x with sample count %u is not supported; skipping.\n", params->desc.format,
params->desc.sample_count);
return;
}
......@@ -269,6 +349,110 @@ static void metal_runner_destroy_resource(struct shader_runner *r, struct resour
free(resource);
};
static bool compile_shader(struct metal_runner *runner, enum shader_type type, struct vkd3d_shader_code *out)
{
struct vkd3d_shader_interface_info interface_info = {.type = VKD3D_SHADER_STRUCTURE_TYPE_INTERFACE_INFO};
struct vkd3d_shader_compile_info info = {.type = VKD3D_SHADER_STRUCTURE_TYPE_COMPILE_INFO};
struct vkd3d_shader_resource_binding bindings[MAX_RESOURCES + MAX_SAMPLERS];
struct vkd3d_shader_compile_option options[3];
struct vkd3d_shader_resource_binding *binding;
struct vkd3d_shader_compile_option *option;
unsigned int descriptor_binding = 0;
bool ret = true;
unsigned int i;
char *messages;
if(!(runner->d3d_blobs[type] = compile_hlsl(&runner->r, type)))
return false;
info.next = &interface_info;
info.source.code = ID3D10Blob_GetBufferPointer(runner->d3d_blobs[type]);
info.source.size = ID3D10Blob_GetBufferSize(runner->d3d_blobs[type]);
if (runner->r.minimum_shader_model < SHADER_MODEL_4_0)
info.source_type = VKD3D_SHADER_SOURCE_D3D_BYTECODE;
else
info.source_type = VKD3D_SHADER_SOURCE_DXBC_TPF;
info.target_type = VKD3D_SHADER_TARGET_MSL;
info.options = options;
info.option_count = 0;
option = &options[info.option_count++];
option->name = VKD3D_SHADER_COMPILE_OPTION_FEATURE;
option->value = shader_runner_caps_get_feature_flags(&runner->caps);
if (runner->r.uniform_count)
{
binding = &bindings[interface_info.binding_count++];
binding->type = VKD3D_SHADER_DESCRIPTOR_TYPE_CBV;
binding->register_space = 0;
binding->register_index = 0;
binding->shader_visibility = VKD3D_SHADER_VISIBILITY_ALL;
binding->flags = VKD3D_SHADER_BINDING_FLAG_BUFFER;
binding->binding.set = 0;
binding->binding.binding = descriptor_binding++;
binding->binding.count = 1;
}
for (i = 0; i < runner->r.resource_count; ++i)
{
struct metal_resource *resource = metal_resource(runner->r.resources[i]);
switch (resource->r.desc.type)
{
case RESOURCE_TYPE_RENDER_TARGET:
case RESOURCE_TYPE_DEPTH_STENCIL:
case RESOURCE_TYPE_VERTEX_BUFFER:
break;
case RESOURCE_TYPE_TEXTURE:
case RESOURCE_TYPE_UAV:
skip("Unhandled texture/uav resource.\n");
ret = false;
break;
}
}
for (i = 0; i < runner->r.sampler_count; ++i)
{
skip("Unhandled sampler state.\n");
ret = false;
}
interface_info.bindings = bindings;
interface_info.next = &runner->signatures[type];
runner->signatures[type].type = VKD3D_SHADER_STRUCTURE_TYPE_SCAN_SIGNATURE_INFO;
runner->signatures[type].next = NULL;
/* vkd3d_shader_compile always executes no matter what value `ret` is */
ret = (vkd3d_shader_compile(&info, out, &messages) >= 0) && ret;
if (messages && vkd3d_test_state.debug_level)
trace("%s\n", messages);
vkd3d_shader_free_messages(messages);
return ret;
}
static id<MTLFunction> compile_stage(struct metal_runner *runner, enum shader_type type)
{
struct vkd3d_shader_code out;
id<MTLFunction> function;
id<MTLLibrary> library;
NSString *src;
NSError *err;
if (!compile_shader(runner, type, &out))
return nil;
src = [[[NSString alloc] initWithBytes:out.code length:out.size encoding:NSUTF8StringEncoding] autorelease];
library = [[runner->device newLibraryWithSource:src options:nil error:&err] autorelease];
ok(library, "Failed to create MTLLibrary: %s.\n", [err.description cStringUsingEncoding:NSUTF8StringEncoding]);
function = [library newFunctionWithName:@"shader_entry"];
ok(function, "Failed to create MTLFunction.\n");
vkd3d_shader_free_shader_code(&out);
return [function autorelease];
}
static bool metal_runner_dispatch(struct shader_runner *r, unsigned int x, unsigned int y, unsigned int z)
{
return false;
......@@ -314,7 +498,243 @@ static void metal_runner_clear(struct shader_runner *r, struct resource *res, co
static bool metal_runner_draw(struct shader_runner *r, D3D_PRIMITIVE_TOPOLOGY primitive_topology,
unsigned int vertex_count, unsigned int instance_count)
{
return false;
MTLViewport viewport = {0, 0, RENDER_TARGET_WIDTH, RENDER_TARGET_HEIGHT, 0.0f, 1.0f};
struct metal_runner *runner = metal_runner(r);
/* [[buffer(0)]] used by descriptor argument buffer */
unsigned int vertex_buffer_binding_offset = 1;
id<MTLArgumentEncoder> vs_argenc = nil;
id<MTLArgumentEncoder> ps_argenc = nil;
id<MTLDevice> device = runner->device;
MTLPrimitiveType primitive_type;
id<MTLRenderPipelineState> pso;
id<MTLTexture> rtvs[8] = {0};
unsigned int rtv_count = 0;
id<MTLBuffer> argbuf = nil;
id<MTLTexture> dsv = nil;
id<MTLBuffer> cb = nil;
NSError* err = nil;
bool ret = false;
unsigned int i;
if (!get_metal_primitive_type(primitive_topology, &primitive_type))
{
skip("Unhandled primitive topology %u.\n", primitive_topology);
return false;
}
for (i = 0; i < runner->r.resource_count; ++i)
{
struct metal_resource *resource = metal_resource(runner->r.resources[i]);
switch (resource->r.desc.type)
{
case RESOURCE_TYPE_RENDER_TARGET:
rtvs[resource->r.desc.slot] = resource->texture;
rtv_count = max(rtv_count, resource->r.desc.slot + 1);
break;
case RESOURCE_TYPE_DEPTH_STENCIL:
dsv = resource->texture;
break;
default:
break;
}
}
@autoreleasepool
{
MTLRenderPassDescriptor *pass_desc = [MTLRenderPassDescriptor renderPassDescriptor];
id<MTLCommandBuffer> cmdbuf = [runner->queue commandBuffer];
MTLRenderPipelineDescriptor *pipeline_desc;
id<MTLRenderCommandEncoder> encoder;
for (i = 0; i < rtv_count; i++)
{
MTLRenderPassColorAttachmentDescriptor *attachment;
if (!rtvs[i])
continue;
attachment = pass_desc.colorAttachments[i];
attachment.loadAction = MTLLoadActionLoad;
attachment.storeAction = MTLStoreActionStore;
attachment.texture = rtvs[i];
}
if (dsv)
{
pass_desc.depthAttachment.texture = dsv;
pass_desc.depthAttachment.loadAction = MTLLoadActionLoad;
pass_desc.depthAttachment.storeAction = MTLStoreActionStore;
}
encoder = [cmdbuf renderCommandEncoderWithDescriptor:pass_desc];
pipeline_desc = [[[MTLRenderPipelineDescriptor alloc] init] autorelease];
for (i = 0; i < runner->r.resource_count; ++i)
{
struct metal_resource *resource = metal_resource(runner->r.resources[i]);
switch (resource->r.desc.type)
{
case RESOURCE_TYPE_RENDER_TARGET:
pipeline_desc.colorAttachments[resource->r.desc.slot].pixelFormat = resource->texture.pixelFormat;
break;
case RESOURCE_TYPE_DEPTH_STENCIL:
{
MTLDepthStencilDescriptor *ds_desc;
id<MTLDepthStencilState> ds_state;
pipeline_desc.depthAttachmentPixelFormat = resource->texture.pixelFormat;
ds_desc = [[MTLDepthStencilDescriptor alloc] init];
ds_desc.depthCompareFunction = get_metal_compare_function(runner->r.depth_func);
ds_desc.depthWriteEnabled = true;
ds_state = [device newDepthStencilStateWithDescriptor:ds_desc];
[encoder setDepthStencilState:ds_state];
[ds_state autorelease];
break;
}
case RESOURCE_TYPE_TEXTURE:
case RESOURCE_TYPE_UAV:
case RESOURCE_TYPE_VERTEX_BUFFER:
break;
}
}
if (!(pipeline_desc.vertexFunction = compile_stage(runner, SHADER_TYPE_VS)))
goto out;
if (!(pipeline_desc.fragmentFunction = compile_stage(runner, SHADER_TYPE_PS)))
goto out;
if (runner->r.shader_source[SHADER_TYPE_HS])
{
skip("Tessellation is not implemented in Metal runner.\n");
goto out;
}
if (runner->r.shader_source[SHADER_TYPE_GS])
{
skip("Geometry shader is not implemented in Metal runner.\n");
goto out;
}
vs_argenc = [[pipeline_desc.vertexFunction newArgumentEncoderWithBufferIndex:0] autorelease];
ps_argenc = [[pipeline_desc.fragmentFunction newArgumentEncoderWithBufferIndex:0] autorelease];
if (runner->r.input_element_count)
{
MTLVertexDescriptor *vertex_desc = [MTLVertexDescriptor vertexDescriptor];
if (runner->r.input_element_count > 32)
fatal_error("Input element count %zu is too high.\n", runner->r.input_element_count);
for (i = 0; i < runner->r.input_element_count; ++i)
{
const struct input_element *element = &runner->r.input_elements[i];
const struct vkd3d_shader_signature_element *signature_element;
MTLVertexAttributeDescriptor *attribute;
signature_element = vkd3d_shader_find_signature_element(&runner->signatures[SHADER_TYPE_VS].input, element->name,
element->index, 0);
ok(signature_element, "Cannot find signature element %s%u.\n", element->name, element->index);
attribute = [vertex_desc.attributes objectAtIndexedSubscript:signature_element->register_index];
attribute.bufferIndex = element->slot + vertex_buffer_binding_offset;
attribute.format = get_metal_attribute_format(element->format);
ok(attribute.format != MTLVertexFormatInvalid, "Unhandled attribute format %u.\n", element->format);
}
for (i = 0; i < runner->r.resource_count; ++i)
{
const struct metal_resource *resource = metal_resource(runner->r.resources[i]);
switch (resource->r.desc.type)
{
case RESOURCE_TYPE_VERTEX_BUFFER:
{
unsigned int j;
MTLVertexBufferLayoutDescriptor *binding = [vertex_desc.layouts
objectAtIndexedSubscript:resource->r.desc.slot + vertex_buffer_binding_offset];
binding.stepFunction = MTLVertexStepFunctionPerVertex;
binding.stride = 0;
for (j = 0; j < runner->r.input_element_count; ++j)
{
if (runner->r.input_elements[j].slot == resource->r.desc.slot)
{
const struct vkd3d_shader_signature_element *signature_element;
MTLVertexAttributeDescriptor *attribute;
signature_element = vkd3d_shader_find_signature_element(
&runner->signatures[SHADER_TYPE_VS].input,
runner->r.input_elements[j].name, runner->r.input_elements[j].index, 0);
attribute = [vertex_desc.attributes
objectAtIndexedSubscript:signature_element->register_index];
attribute.offset = binding.stride;
binding.stride += runner->r.input_elements[j].texel_size;
}
}
[encoder setVertexBuffer:resource->buffer
offset:0
atIndex:resource->r.desc.slot + vertex_buffer_binding_offset];
break;
}
default:
break;
}
}
pipeline_desc.vertexDescriptor = vertex_desc;
}
if (runner->r.sample_mask != ~0u)
fatal_error("SampleMask %#x not supported.\n", runner->r.sample_mask);
pso = [[device newRenderPipelineStateWithDescriptor:pipeline_desc error:&err] autorelease];
ok(pso, "Failed to compile pipeline state: %s.\n", [err.description cStringUsingEncoding:NSUTF8StringEncoding]);
[encoder setRenderPipelineState:pso];
/* resource binding */
argbuf = [[device newBufferWithLength:vs_argenc.encodedLength + ps_argenc.encodedLength
options:DEFAULT_BUFFER_RESOURCE_OPTION] autorelease];
[vs_argenc setArgumentBuffer:argbuf offset:0];
[encoder setVertexBuffer:argbuf offset:0 atIndex:0];
[ps_argenc setArgumentBuffer:argbuf offset:vs_argenc.encodedLength];
[encoder setFragmentBuffer:argbuf offset:vs_argenc.encodedLength atIndex:0];
/* we won't deal with texture/uav/sampler binding here, as the draw call fails at `compile_stage` */
if (r->uniform_count)
{
cb = [[device newBufferWithBytes:r->uniforms
length:runner->r.uniform_count * sizeof(*runner->r.uniforms)
options:DEFAULT_BUFFER_RESOURCE_OPTION] autorelease];
[vs_argenc setBuffer:cb offset:0 atIndex:0];
[ps_argenc setBuffer:cb offset:0 atIndex:0];
[encoder useResource:cb usage:MTLResourceUsageRead stages:MTLRenderStageVertex | MTLRenderStageFragment];
}
[encoder setViewport:viewport];
[encoder setTriangleFillMode:MTLTriangleFillModeFill];
[encoder setCullMode:MTLCullModeNone];
[encoder setFrontFacingWinding:MTLWindingClockwise];
[encoder setDepthBias:0 slopeScale:0 clamp:0];
[encoder setDepthClipMode:MTLDepthClipModeClip];
[encoder drawPrimitives:primitive_type
vertexStart:0
vertexCount:vertex_count
instanceCount:instance_count];
ret = true;
out:
[encoder endEncoding];
[cmdbuf commit];
[cmdbuf waitUntilCompleted];
}
for (i = 0; i < SHADER_TYPE_COUNT; ++i)
{
if (!runner->d3d_blobs[i])
continue;
vkd3d_shader_free_scan_signature_info(&runner->signatures[i]);
ID3D10Blob_Release(runner->d3d_blobs[i]);
runner->d3d_blobs[i] = NULL;
}
return ret;
};
static bool metal_runner_copy(struct shader_runner *r, struct resource *src, struct resource *dst)
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment