Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
112 changes: 112 additions & 0 deletions src/libs/dxmt-0.60/src/winemetal/unix/winemetal_unix.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
# import <MetalFX/MetalFX.h>
#endif
#import <QuartzCore/QuartzCore.h>
#include <inttypes.h>
#include <objc/runtime.h>
#include <stdio.h>
#include "objc/objc-runtime.h"
#define WINEMETAL_API
#include "../winemetal_thunks.h"
Expand All @@ -23,6 +26,74 @@ execute_on_main(dispatch_block_t block) {
}
}

static bool
validate_indexed_draw_buffer(
obj_handle_t buffer_handle, enum WMTIndexType index_type, uint64_t index_buffer_offset, uint64_t index_count,
const char *command_name
) {
id<MTLBuffer> index_buffer = (id<MTLBuffer>)buffer_handle;
if (!index_buffer) {
fprintf(stderr, "DXMT: skipping %s with null index buffer\n", command_name);
return false;
}
if (index_type != WMTIndexTypeUInt16 && index_type != WMTIndexTypeUInt32) {
fprintf(stderr, "DXMT: skipping %s with invalid index type %u\n", command_name, (unsigned)index_type);
return false;
}

const uint64_t index_size = index_type == WMTIndexTypeUInt32 ? 4 : 2;
const uint64_t index_buffer_length = [index_buffer length];
if (index_count > (UINT64_MAX - index_buffer_offset) / index_size) {
fprintf(
stderr,
"DXMT: skipping %s with overflowing index range offset=%" PRIu64 " count=%" PRIu64 " index_size=%" PRIu64
" length=%" PRIu64 "\n",
command_name, index_buffer_offset, index_count, index_size, index_buffer_length
);
return false;
}

const uint64_t index_range_end = index_buffer_offset + index_count * index_size;
if (index_buffer_offset > index_buffer_length || index_range_end > index_buffer_length) {
fprintf(
stderr,
"DXMT: skipping %s with out-of-bounds index range offset=%" PRIu64 " count=%" PRIu64
" index_size=%" PRIu64 " length=%" PRIu64 "\n",
command_name, index_buffer_offset, index_count, index_size, index_buffer_length
);
return false;
}

return true;
}

static char g_render_encoder_has_pipeline_state_key;

static bool
render_encoder_has_pipeline_state(id<MTLRenderCommandEncoder> encoder) {
NSNumber *state = objc_getAssociatedObject(encoder, &g_render_encoder_has_pipeline_state_key);
return state ? [state boolValue] : false;
}

static void
render_encoder_set_pipeline_state_valid(id<MTLRenderCommandEncoder> encoder, bool valid) {
objc_setAssociatedObject(
encoder, &g_render_encoder_has_pipeline_state_key, [NSNumber numberWithBool:valid], OBJC_ASSOCIATION_RETAIN_NONATOMIC
);
}

static bool
validate_render_pipeline_state(bool pipeline_state_valid, bool *reported_missing_pso) {
if (pipeline_state_valid)
return true;

if (!*reported_missing_pso) {
fprintf(stderr, "DXMT: skipping render draws without a valid pipeline state\n");
*reported_missing_pso = true;
}
return false;
}

static NTSTATUS
_NSObject_retain(NSObject **obj) {
[*obj retain];
Expand Down Expand Up @@ -738,6 +809,8 @@ _MTLRenderCommandEncoder_encodeCommands(void *obj) {
struct unixcall_generic_obj_cmd_noret *params = obj;
const struct wmtcmd_base *next = params->cmd_head.ptr;
id<MTLRenderCommandEncoder> encoder = (id<MTLRenderCommandEncoder>)params->encoder;
bool render_pipeline_state_valid = render_encoder_has_pipeline_state(encoder);
bool reported_missing_pso = false;
while (next) {
switch ((enum WMTRenderCommandType)next->type) {
default:
Expand Down Expand Up @@ -823,7 +896,15 @@ _MTLRenderCommandEncoder_encodeCommands(void *obj) {
}
case WMTRenderCommandSetPSO: {
struct wmtcmd_render_setpso *body = (struct wmtcmd_render_setpso *)next;
if (!body->pso) {
render_pipeline_state_valid = false;
render_encoder_set_pipeline_state_valid(encoder, false);
fprintf(stderr, "DXMT: skipping null render pipeline state\n");
break;
}
[encoder setRenderPipelineState:(id<MTLRenderPipelineState>)body->pso];
render_pipeline_state_valid = true;
render_encoder_set_pipeline_state_valid(encoder, true);
break;
}
case WMTRenderCommandSetDSSO: {
Expand All @@ -845,6 +926,8 @@ _MTLRenderCommandEncoder_encodeCommands(void *obj) {
}
case WMTRenderCommandDraw: {
struct wmtcmd_render_draw *body = (struct wmtcmd_render_draw *)next;
if (!validate_render_pipeline_state(render_pipeline_state_valid, &reported_missing_pso))
break;
[encoder drawPrimitives:(MTLPrimitiveType)body->primitive_type
vertexStart:body->vertex_start
vertexCount:body->vertex_count
Expand All @@ -854,6 +937,12 @@ _MTLRenderCommandEncoder_encodeCommands(void *obj) {
}
case WMTRenderCommandDrawIndexed: {
struct wmtcmd_render_draw_indexed *body = (struct wmtcmd_render_draw_indexed *)next;
if (!validate_render_pipeline_state(render_pipeline_state_valid, &reported_missing_pso))
break;
if (!validate_indexed_draw_buffer(
body->index_buffer, body->index_type, body->index_buffer_offset, body->index_count, "DrawIndexed"
))
break;
[encoder drawIndexedPrimitives:(MTLPrimitiveType)body->primitive_type
indexCount:body->index_count
indexType:(MTLIndexType)body->index_type
Expand All @@ -866,13 +955,32 @@ _MTLRenderCommandEncoder_encodeCommands(void *obj) {
}
case WMTRenderCommandDrawIndirect: {
struct wmtcmd_render_draw_indirect *body = (struct wmtcmd_render_draw_indirect *)next;
if (!validate_render_pipeline_state(render_pipeline_state_valid, &reported_missing_pso))
break;
[encoder drawPrimitives:(MTLPrimitiveType)body->primitive_type
indirectBuffer:(id<MTLBuffer>)body->indirect_args_buffer
indirectBufferOffset:body->indirect_args_offset];
break;
}
case WMTRenderCommandDrawIndexedIndirect: {
struct wmtcmd_render_draw_indexed_indirect *body = (struct wmtcmd_render_draw_indexed_indirect *)next;
if (!validate_render_pipeline_state(render_pipeline_state_valid, &reported_missing_pso))
break;
if (!validate_indexed_draw_buffer(
body->index_buffer, body->index_type, body->index_buffer_offset, 0, "DrawIndexedIndirect"
))
break;
id<MTLBuffer> indirect_args_buffer = (id<MTLBuffer>)body->indirect_args_buffer;
if (!indirect_args_buffer || body->indirect_args_offset > [indirect_args_buffer length] ||
[indirect_args_buffer length] - body->indirect_args_offset < 5 * sizeof(uint32_t)) {
fprintf(
stderr,
"DXMT: skipping DrawIndexedIndirect with invalid indirect buffer offset=%" PRIu64 " length=%" PRIu64
"\n",
body->indirect_args_offset, indirect_args_buffer ? (uint64_t)[indirect_args_buffer length] : 0
);
break;
}
[encoder drawIndexedPrimitives:(MTLPrimitiveType)body->primitive_type
indexType:(MTLIndexType)body->index_type
indexBuffer:(id<MTLBuffer>)body->index_buffer
Expand All @@ -883,6 +991,8 @@ _MTLRenderCommandEncoder_encodeCommands(void *obj) {
}
case WMTRenderCommandDrawMeshThreadgroups: {
struct wmtcmd_render_draw_meshthreadgroups *body = (struct wmtcmd_render_draw_meshthreadgroups *)next;
if (!validate_render_pipeline_state(render_pipeline_state_valid, &reported_missing_pso))
break;
[encoder drawMeshThreadgroups:MTLSizeMake(
body->threadgroup_per_grid.width, body->threadgroup_per_grid.height,
body->threadgroup_per_grid.depth
Expand All @@ -900,6 +1010,8 @@ _MTLRenderCommandEncoder_encodeCommands(void *obj) {
case WMTRenderCommandDrawMeshThreadgroupsIndirect: {
struct wmtcmd_render_draw_meshthreadgroups_indirect *body =
(struct wmtcmd_render_draw_meshthreadgroups_indirect *)next;
if (!validate_render_pipeline_state(render_pipeline_state_valid, &reported_missing_pso))
break;
[encoder drawMeshThreadgroupsWithIndirectBuffer:(id<MTLBuffer>)body->indirect_args_buffer
indirectBufferOffset:body->indirect_args_offset
threadsPerObjectThreadgroup:MTLSizeMake(
Expand Down