diff --git a/libs/vkd3d-shader/spirv.c b/libs/vkd3d-shader/spirv.c index 3dd32aab..8c1c61bc 100644 --- a/libs/vkd3d-shader/spirv.c +++ b/libs/vkd3d-shader/spirv.c @@ -23,12 +23,61 @@ static void vkd3d_spirv_dump(const struct vkd3d_shader_code *spirv) {} static void vkd3d_spirv_validate(const struct vkd3d_shader_code *spirv) {} +struct vkd3d_spirv_stream +{ + uint32_t *words; + size_t capacity; + size_t word_count; +}; + +static void vkd3d_spirv_stream_init(struct vkd3d_spirv_stream *stream) +{ + stream->capacity = 256; + if (!(stream->words = vkd3d_calloc(stream->capacity, sizeof(*stream->words)))) + stream->capacity = 0; + stream->word_count = 0; +} + +static void vkd3d_spirv_stream_free(struct vkd3d_spirv_stream *stream) +{ + vkd3d_free(stream->words); +} + +static bool vkd3d_spirv_stream_append(struct vkd3d_spirv_stream *dst_stream, + const struct vkd3d_spirv_stream *src_stream) +{ + if (!vkd3d_array_reserve((void **)&dst_stream->words, &dst_stream->capacity, + dst_stream->word_count + src_stream->word_count, sizeof(*dst_stream->words))) + return false; + + assert(dst_stream->word_count + src_stream->word_count <= dst_stream->capacity); + memcpy(&dst_stream->words[dst_stream->word_count], src_stream->words, + src_stream->word_count * sizeof(*src_stream->words)); + dst_stream->word_count += src_stream->word_count; + return true; +} + struct vkd3d_spirv_builder { uint64_t capability_mask; SpvExecutionModel execution_model; uint32_t current_id; + uint32_t main_function_id; + uint32_t type_id[VKD3D_TYPE_COUNT][VKD3D_VEC4_SIZE]; + + struct vkd3d_spirv_stream debug_stream; /* debug instructions */ + struct vkd3d_spirv_stream annotation_stream; /* decoration instructions */ + struct vkd3d_spirv_stream global_stream; /* types, constants, global variables */ + struct vkd3d_spirv_stream function_stream; /* function definitions */ + + union + { + struct + { + uint32_t local_size[3]; + } compute; + } u; }; static void vkd3d_spirv_enable_capability(struct vkd3d_spirv_builder *builder, @@ -62,17 +111,369 @@ static void vkd3d_spirv_set_execution_model(struct vkd3d_spirv_builder *builder, } } +static uint32_t vkd3d_spirv_opcode_word(SpvOp op, unsigned int word_count) +{ + assert(!(op & ~SpvOpCodeMask)); + return (word_count << SpvWordCountShift) | op; +} + +static void vkd3d_spirv_build_word(struct vkd3d_spirv_stream *stream, uint32_t word) +{ + if (!vkd3d_array_reserve((void **)&stream->words, &stream->capacity, + stream->word_count + 1, sizeof(*stream->words))) + return; + + stream->words[stream->word_count++] = word; +} + +static unsigned int vkd3d_spirv_string_word_count(const char *str) +{ + return align(strlen(str) + 1, sizeof(uint32_t)) / sizeof(uint32_t); +} + +static void vkd3d_spirv_build_string(struct vkd3d_spirv_stream *stream, + const char *str, unsigned int word_count) +{ + unsigned int word_idx, i; + const char *ptr = str; + + for (word_idx = 0; word_idx < word_count; ++word_idx) + { + uint32_t word = 0; + for (i = 0; i < sizeof(uint32_t) && *ptr; ++i) + word |= (uint32_t)*ptr++ << (8 * i); + vkd3d_spirv_build_word(stream, word); + } +} + +/* + * vkd3d_spirv_build_op[1-3][v]() + * vkd3d_spirv_build_op_[t][r][1-3][v]() + * + * t - result type + * r - result id + * 1-3 - the number of operands + * v - variable number of operands + */ +static void vkd3d_spirv_build_op(struct vkd3d_spirv_stream *stream, SpvOp op) +{ + vkd3d_spirv_build_word(stream, vkd3d_spirv_opcode_word(op, 1)); +} + +static void vkd3d_spirv_build_op1(struct vkd3d_spirv_stream *stream, + SpvOp op, uint32_t operand) +{ + vkd3d_spirv_build_word(stream, vkd3d_spirv_opcode_word(op, 2)); + vkd3d_spirv_build_word(stream, operand); +} + +static void vkd3d_spirv_build_op1v(struct vkd3d_spirv_stream *stream, + SpvOp op, uint32_t operand0, const uint32_t *operands, unsigned int operand_count) +{ + unsigned int i; + vkd3d_spirv_build_word(stream, vkd3d_spirv_opcode_word(op, 2 + operand_count)); + vkd3d_spirv_build_word(stream, operand0); + for (i = 0; i < operand_count; ++i) + vkd3d_spirv_build_word(stream, operands[i]); +} + +static void vkd3d_spirv_build_op2v(struct vkd3d_spirv_stream *stream, + SpvOp op, uint32_t operand0, uint32_t operand1, + const uint32_t *operands, unsigned int operand_count) +{ + unsigned int i; + vkd3d_spirv_build_word(stream, vkd3d_spirv_opcode_word(op, 3 + operand_count)); + vkd3d_spirv_build_word(stream, operand0); + vkd3d_spirv_build_word(stream, operand1); + for (i = 0; i < operand_count; ++i) + vkd3d_spirv_build_word(stream, operands[i]); +} + +static void vkd3d_spirv_build_op2(struct vkd3d_spirv_stream *stream, + SpvOp op, uint32_t operand0, uint32_t operand1) +{ + return vkd3d_spirv_build_op2v(stream, op, operand0, operand1, NULL, 0); +} + +static uint32_t vkd3d_spirv_build_op_rv(struct vkd3d_spirv_builder *builder, + struct vkd3d_spirv_stream *stream, SpvOp op, + const uint32_t *operands, unsigned int operand_count) +{ + uint32_t result_id = builder->current_id++; + vkd3d_spirv_build_op1v(stream, op, result_id, operands, operand_count); + return result_id; +} + +static uint32_t vkd3d_spirv_build_op_r(struct vkd3d_spirv_builder *builder, + struct vkd3d_spirv_stream *stream, SpvOp op) +{ + return vkd3d_spirv_build_op_rv(builder, stream, op, NULL, 0); +} + +static uint32_t vkd3d_spirv_build_op_r1(struct vkd3d_spirv_builder *builder, + struct vkd3d_spirv_stream *stream, SpvOp op, uint32_t operand0) +{ + return vkd3d_spirv_build_op_rv(builder, stream, op, &operand0, 1); +} + +static uint32_t vkd3d_spirv_build_op_r2(struct vkd3d_spirv_builder *builder, + struct vkd3d_spirv_stream *stream, SpvOp op, uint32_t operand0, uint32_t operand1) +{ + uint32_t operands[] = {operand0, operand1}; + return vkd3d_spirv_build_op_rv(builder, stream, op, operands, ARRAY_SIZE(operands)); +} + +static uint32_t vkd3d_spirv_build_op_r1v(struct vkd3d_spirv_builder *builder, + struct vkd3d_spirv_stream *stream, SpvOp op, uint32_t operand0, + const uint32_t *operands, unsigned int operand_count) +{ + uint32_t result_id = builder->current_id++; + vkd3d_spirv_build_op2v(stream, op, result_id, operand0, operands, operand_count); + return result_id; +} + +static uint32_t vkd3d_spirv_build_op_trv(struct vkd3d_spirv_builder *builder, + struct vkd3d_spirv_stream *stream, SpvOp op, uint32_t result_type, + const uint32_t *operands, unsigned int operand_count) +{ + uint32_t result_id = builder->current_id++; + vkd3d_spirv_build_op2v(stream, op, result_type, result_id, operands, operand_count); + return result_id; +} + +static uint32_t vkd3d_spirv_build_op_tr2(struct vkd3d_spirv_builder *builder, + struct vkd3d_spirv_stream *stream, SpvOp op, uint32_t result_type, + uint32_t operand0, uint32_t operand1) +{ + uint32_t operands[] = {operand0, operand1}; + return vkd3d_spirv_build_op_trv(builder, stream, op, result_type, + operands, ARRAY_SIZE(operands)); +} + +static void vkd3d_spirv_build_op_capability(struct vkd3d_spirv_stream *stream, + SpvCapability cap) +{ + vkd3d_spirv_build_op1(stream, SpvOpCapability, cap); +} + +static void vkd3d_spirv_build_op_memory_model(struct vkd3d_spirv_stream *stream, + SpvAddressingModel addressing_model, SpvMemoryModel memory_model) +{ + vkd3d_spirv_build_op2(stream, SpvOpMemoryModel, addressing_model, memory_model); +} + +static void vkd3d_spirv_build_op_entry_point(struct vkd3d_spirv_stream *stream, + SpvExecutionModel model, uint32_t function_id, const char *name, + uint32_t *interface_list, unsigned int interface_size) +{ + unsigned int i, name_size = vkd3d_spirv_string_word_count(name); + vkd3d_spirv_build_word(stream, vkd3d_spirv_opcode_word(SpvOpEntryPoint, 3 + name_size + interface_size)); + vkd3d_spirv_build_word(stream, model); + vkd3d_spirv_build_word(stream, function_id); + vkd3d_spirv_build_string(stream, name, name_size); + for (i = 0; i < interface_size; ++i) + vkd3d_spirv_build_word(stream, interface_list[i]); +} + +static void vkd3d_spirv_build_op_execution_mode(struct vkd3d_spirv_stream *stream, + uint32_t entry_point, SpvExecutionMode mode, uint32_t *literals, uint32_t literal_count) +{ + vkd3d_spirv_build_op2v(stream, SpvOpExecutionMode, entry_point, mode, literals, literal_count); +} + +static void vkd3d_spirv_build_op_name(struct vkd3d_spirv_builder *builder, + uint32_t id, const char *name) +{ + unsigned int name_size = vkd3d_spirv_string_word_count(name); + struct vkd3d_spirv_stream *stream = &builder->debug_stream; + vkd3d_spirv_build_word(stream, vkd3d_spirv_opcode_word(SpvOpName, 2 + name_size)); + vkd3d_spirv_build_word(stream, id); + vkd3d_spirv_build_string(stream, name, name_size); +} + +static uint32_t vkd3d_spirv_build_op_type_void(struct vkd3d_spirv_builder *builder) +{ + return vkd3d_spirv_build_op_r(builder, &builder->global_stream, SpvOpTypeVoid); +} + +static uint32_t vkd3d_spirv_build_op_type_float(struct vkd3d_spirv_builder *builder, + uint32_t width) +{ + return vkd3d_spirv_build_op_r1(builder, &builder->global_stream, SpvOpTypeFloat, width); +} + +static uint32_t vkd3d_spirv_build_op_type_int(struct vkd3d_spirv_builder *builder, + uint32_t width, uint32_t signedness) +{ + return vkd3d_spirv_build_op_r2(builder, &builder->global_stream, SpvOpTypeInt, width, signedness); +} + +static uint32_t vkd3d_spirv_build_op_type_vector(struct vkd3d_spirv_builder *builder, + uint32_t component_type, uint32_t component_count) +{ + return vkd3d_spirv_build_op_r2(builder, &builder->global_stream, + SpvOpTypeVector, component_type, component_count); +} + +static uint32_t vkd3d_spirv_build_op_type_function(struct vkd3d_spirv_builder *builder, + uint32_t return_type, uint32_t *param_types, unsigned int param_count) +{ + return vkd3d_spirv_build_op_r1v(builder, &builder->global_stream, + SpvOpTypeFunction, return_type, param_types, param_count); +} + +static uint32_t vkd3d_spirv_build_op_function(struct vkd3d_spirv_builder *builder, + uint32_t result_type, uint32_t function_control, uint32_t function_type) +{ + return vkd3d_spirv_build_op_tr2(builder, &builder->function_stream, + SpvOpFunction, result_type, function_control, function_type); +} + +static void vkd3d_spirv_build_op_function_end(struct vkd3d_spirv_builder *builder) +{ + vkd3d_spirv_build_op(&builder->function_stream, SpvOpFunctionEnd); +} + +static uint32_t vkd3d_spirv_build_op_label(struct vkd3d_spirv_builder *builder) +{ + return vkd3d_spirv_build_op_r(builder, &builder->function_stream, SpvOpLabel); +} + +static void vkd3d_spirv_build_op_return(struct vkd3d_spirv_builder *builder) +{ + vkd3d_spirv_build_op(&builder->function_stream, SpvOpReturn); +} + +static uint32_t vkd3d_spirv_get_type_id(struct vkd3d_spirv_builder *builder, + enum vkd3d_component_type component_type, unsigned int component_count) +{ + uint32_t id, scalar_id; + + assert(0 < component_count && component_count <= VKD3D_VEC4_SIZE); + + if (!(id = builder->type_id[component_type][component_count - 1])) + { + if (component_count == 1) + { + switch (component_type) + { + case VKD3D_TYPE_VOID: + id = vkd3d_spirv_build_op_type_void(builder); + break; + case VKD3D_TYPE_FLOAT: + id = vkd3d_spirv_build_op_type_float(builder, 32); + break; + case VKD3D_TYPE_INT: + case VKD3D_TYPE_UINT: + id = vkd3d_spirv_build_op_type_int(builder, 32, component_type == VKD3D_TYPE_INT); + break; + default: + FIXME("Unhandled component type %#x.\n", component_type); + id = 0; + } + } + else + { + assert(component_type != VKD3D_TYPE_VOID); + scalar_id = vkd3d_spirv_get_type_id(builder, component_type, 1); + id = vkd3d_spirv_build_op_type_vector(builder, scalar_id, component_count); + } + + builder->type_id[component_type][component_count - 1] = id; + } + + return id; +} + static void vkd3d_spirv_builder_init(struct vkd3d_spirv_builder *builder) { + uint32_t void_id, function_type_id; + + vkd3d_spirv_stream_init(&builder->debug_stream); + vkd3d_spirv_stream_init(&builder->annotation_stream); + vkd3d_spirv_stream_init(&builder->global_stream); + vkd3d_spirv_stream_init(&builder->function_stream); + builder->current_id = 1; + + void_id = vkd3d_spirv_get_type_id(builder, VKD3D_TYPE_VOID, 1); + function_type_id = vkd3d_spirv_build_op_type_function(builder, void_id, NULL, 0); + + builder->main_function_id = vkd3d_spirv_build_op_function(builder, void_id, + SpvFunctionControlMaskNone, function_type_id); + vkd3d_spirv_build_op_name(builder, builder->main_function_id, "main"); + vkd3d_spirv_build_op_label(builder); +} + +static void vkd3d_spirv_builder_free(struct vkd3d_spirv_builder *builder) +{ + vkd3d_spirv_stream_free(&builder->debug_stream); + vkd3d_spirv_stream_free(&builder->annotation_stream); + vkd3d_spirv_stream_free(&builder->global_stream); + vkd3d_spirv_stream_free(&builder->function_stream); +} + +static void vkd3d_spirv_build_execution_mode_declarations(struct vkd3d_spirv_builder *builder, + struct vkd3d_spirv_stream *stream) +{ + if (builder->execution_model == SpvExecutionModelGLCompute) + { + vkd3d_spirv_build_op_execution_mode(stream, builder->main_function_id, SpvExecutionModeLocalSize, + builder->u.compute.local_size, ARRAY_SIZE(builder->u.compute.local_size)); + } } static bool vkd3d_spirv_compile_module(struct vkd3d_spirv_builder *builder, struct vkd3d_shader_code *spirv) { - FIXME("Not implemented!\n"); + uint64_t capability_mask = builder->capability_mask; + struct vkd3d_spirv_stream stream; + uint32_t *code; + unsigned int i; + size_t size; - return false; + vkd3d_spirv_build_op_function_end(builder); + + vkd3d_spirv_stream_init(&stream); + + vkd3d_spirv_build_word(&stream, SpvMagicNumber); + vkd3d_spirv_build_word(&stream, SpvVersion); + vkd3d_spirv_build_word(&stream, 0); /* generator */ + vkd3d_spirv_build_word(&stream, builder->current_id); /* bound */ + vkd3d_spirv_build_word(&stream, 0); /* schema, reserved */ + + for (i = 0; capability_mask; ++i) + { + if (capability_mask & 1) + vkd3d_spirv_build_op_capability(&stream, i); + capability_mask >>= 1; + } + + vkd3d_spirv_build_op_memory_model(&stream, SpvAddressingModelLogical, SpvMemoryModelGLSL450); + vkd3d_spirv_build_op_entry_point(&stream, builder->execution_model, builder->main_function_id, + "main", NULL, 0); + + vkd3d_spirv_build_execution_mode_declarations(builder, &stream); + + vkd3d_spirv_stream_append(&stream, &builder->debug_stream); + vkd3d_spirv_stream_append(&stream, &builder->annotation_stream); + vkd3d_spirv_stream_append(&stream, &builder->global_stream); + vkd3d_spirv_stream_append(&stream, &builder->function_stream); + + if (!(code = vkd3d_calloc(stream.word_count, sizeof(*code)))) + { + vkd3d_spirv_stream_free(&stream); + return false; + } + + size = stream.word_count * sizeof(*code); + memcpy(code, stream.words, size); + vkd3d_spirv_stream_free(&stream); + + spirv->code = code; + spirv->size = size; + + return true; } struct vkd3d_dxbc_compiler @@ -121,10 +522,23 @@ struct vkd3d_dxbc_compiler *vkd3d_dxbc_compiler_create(const struct vkd3d_shader return compiler; } +static void vkd3d_dxbc_compiler_emit_return(struct vkd3d_dxbc_compiler *compiler, + const struct vkd3d_shader_instruction *instruction) +{ + vkd3d_spirv_build_op_return(&compiler->spirv_builder); +} + void vkd3d_dxbc_compiler_handle_instruction(struct vkd3d_dxbc_compiler *compiler, const struct vkd3d_shader_instruction *instruction) { - FIXME("Unhandled instruction %#x.\n", instruction->handler_idx); + switch (instruction->handler_idx) + { + case VKD3DSIH_RET: + vkd3d_dxbc_compiler_emit_return(compiler, instruction); + break; + default: + FIXME("Unhandled instruction %#x.\n", instruction->handler_idx); + } } bool vkd3d_dxbc_compiler_generate_spirv(struct vkd3d_dxbc_compiler *compiler, @@ -144,5 +558,7 @@ bool vkd3d_dxbc_compiler_generate_spirv(struct vkd3d_dxbc_compiler *compiler, void vkd3d_dxbc_compiler_destroy(struct vkd3d_dxbc_compiler *compiler) { + vkd3d_spirv_builder_free(&compiler->spirv_builder); + vkd3d_free(compiler); }