diff --git a/dlls/wined3d/Makefile.in b/dlls/wined3d/Makefile.in index 69415ebb7f9..128956c66be 100644 --- a/dlls/wined3d/Makefile.in +++ b/dlls/wined3d/Makefile.in @@ -14,6 +14,7 @@ SOURCES = \ device.c \ directx.c \ ffp_gl.c \ + ffp_hlsl.c \ gl_compat.c \ glsl_shader.c \ palette.c \ diff --git a/dlls/wined3d/adapter_vk.c b/dlls/wined3d/adapter_vk.c index 6f622aae1bc..8c5f75c1316 100644 --- a/dlls/wined3d/adapter_vk.c +++ b/dlls/wined3d/adapter_vk.c @@ -2359,6 +2359,7 @@ static void wined3d_adapter_vk_init_d3d_info(struct wined3d_adapter_vk *adapter_ d3d_info->fences = true; d3d_info->persistent_map = true; d3d_info->gpu_push_constants = true; + d3d_info->ffp_hlsl = true; /* Like GL, Vulkan doesn't explicitly specify a filling convention and only mandates that a * shared edge of two adjacent triangles generate a fragment for exactly one of the triangles. diff --git a/dlls/wined3d/device.c b/dlls/wined3d/device.c index 0fb71108b27..c4558e1f60d 100644 --- a/dlls/wined3d/device.c +++ b/dlls/wined3d/device.c @@ -1667,6 +1667,14 @@ static void device_free_depth_stencil_state(struct wine_rb_entry *entry, void *c wined3d_depth_stencil_state_decref(state); } +static void device_free_ffp_pixel_shader(struct wine_rb_entry *entry, void *context) +{ + struct wined3d_ffp_ps *ps = WINE_RB_ENTRY_VALUE(entry, struct wined3d_ffp_ps, entry); + + wined3d_shader_decref(ps->shader); + free(ps); +} + void wined3d_device_uninit_3d(struct wined3d_device *device) { struct wined3d_state *state = device->cs->c.state; @@ -1714,6 +1722,7 @@ void wined3d_device_uninit_3d(struct wined3d_device *device) wine_rb_destroy(&device->rasterizer_states, device_free_rasterizer_state, NULL); wine_rb_destroy(&device->blend_states, device_free_blend_state, NULL); wine_rb_destroy(&device->depth_stencil_states, device_free_depth_stencil_state, NULL); + wine_rb_destroy(&device->ffp_pixel_shaders, device_free_ffp_pixel_shader, NULL); LIST_FOR_EACH_ENTRY_SAFE(resource, cursor, &device->resources, struct wined3d_resource, resource_list_entry) { @@ -5515,6 +5524,7 @@ HRESULT wined3d_device_init(struct wined3d_device *device, struct wined3d *wined wine_rb_init(&device->rasterizer_states, wined3d_rasterizer_state_compare); wine_rb_init(&device->blend_states, wined3d_blend_state_compare); wine_rb_init(&device->depth_stencil_states, wined3d_depth_stencil_state_compare); + wine_rb_init(&device->ffp_pixel_shaders, wined3d_ffp_frag_program_key_compare); if (vertex_pipeline->vp_states && fragment_pipeline->states && FAILED(hr = compile_state_table(device->state_table, device->multistate_funcs, @@ -5527,6 +5537,7 @@ HRESULT wined3d_device_init(struct wined3d_device *device, struct wined3d *wined wine_rb_destroy(&device->blend_states, NULL, NULL); wine_rb_destroy(&device->depth_stencil_states, NULL, NULL); wine_rb_destroy(&device->so_descs, NULL, NULL); + wine_rb_destroy(&device->ffp_pixel_shaders, NULL, NULL); wined3d_decref(device->wined3d); return hr; } @@ -5554,6 +5565,7 @@ err: wine_rb_destroy(&device->blend_states, NULL, NULL); wine_rb_destroy(&device->depth_stencil_states, NULL, NULL); wine_rb_destroy(&device->so_descs, NULL, NULL); + wine_rb_destroy(&device->ffp_pixel_shaders, NULL, NULL); wined3d_decref(device->wined3d); return hr; } diff --git a/dlls/wined3d/ffp_hlsl.c b/dlls/wined3d/ffp_hlsl.c new file mode 100644 index 00000000000..72a8e702cad --- /dev/null +++ b/dlls/wined3d/ffp_hlsl.c @@ -0,0 +1,97 @@ +/* + * Fixed-function pipeline replacement implemented using HLSL shaders + * + * Copyright 2022,2024 Elizabeth Figura for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "wined3d_private.h" +#include + +WINE_DEFAULT_DEBUG_CHANNEL(d3d_shader); + +static bool ffp_hlsl_generate_pixel_shader(const struct ffp_frag_settings *settings, + struct wined3d_string_buffer *string) +{ + FIXME("Not yet implemented.\n"); + return false; +} + +static bool compile_hlsl_shader(const struct wined3d_string_buffer *hlsl, + struct vkd3d_shader_code *sm1, const char *profile) +{ + struct vkd3d_shader_hlsl_source_info hlsl_source_info = {.type = VKD3D_SHADER_STRUCTURE_TYPE_HLSL_SOURCE_INFO}; + struct vkd3d_shader_compile_info compile_info = {.type = VKD3D_SHADER_STRUCTURE_TYPE_COMPILE_INFO}; + char *messages; + int ret; + + compile_info.source.code = hlsl->buffer; + compile_info.source.size = hlsl->content_size; + compile_info.source_type = VKD3D_SHADER_SOURCE_HLSL; + compile_info.target_type = VKD3D_SHADER_TARGET_D3D_BYTECODE; + compile_info.log_level = VKD3D_SHADER_LOG_WARNING; + + compile_info.next = &hlsl_source_info; + hlsl_source_info.profile = profile; + + ret = vkd3d_shader_compile(&compile_info, sm1, &messages); + if (messages && *messages && FIXME_ON(d3d_shader)) + { + const char *ptr, *end, *line; + + FIXME("Shader log:\n"); + ptr = messages; + end = ptr + strlen(ptr); + while ((line = wined3d_get_line(&ptr, end))) + FIXME(" %.*s", (int)(ptr - line), line); + FIXME("\n"); + } + vkd3d_shader_free_messages(messages); + + if (ret < 0) + { + ERR("Failed to compile HLSL, ret %d.\n", ret); + return false; + } + + return true; +} + +bool ffp_hlsl_compile_ps(const struct ffp_frag_settings *settings, struct wined3d_shader_desc *shader_desc) +{ + struct wined3d_string_buffer string; + struct vkd3d_shader_code sm1; + + if (!string_buffer_init(&string)) + return false; + + if (!ffp_hlsl_generate_pixel_shader(settings, &string)) + { + string_buffer_free(&string); + return false; + } + + if (!compile_hlsl_shader(&string, &sm1, "ps_2_0")) + { + string_buffer_free(&string); + return false; + } + string_buffer_free(&string); + + shader_desc->byte_code = sm1.code; + shader_desc->byte_code_size = ~(size_t)0; + return true; +} diff --git a/dlls/wined3d/shader.c b/dlls/wined3d/shader.c index 3bef05d979d..71dffd75761 100644 --- a/dlls/wined3d/shader.c +++ b/dlls/wined3d/shader.c @@ -2436,7 +2436,8 @@ static HRESULT shader_set_function(struct wined3d_shader *shader, const struct w WARN("Wrong shader type %s.\n", debug_shader_type(reg_maps->shader_version.type)); return WINED3DERR_INVALIDCALL; } - if (version->major > shader_max_version_from_feature_level(shader->device->cs->c.state->feature_level)) + if (!shader->is_ffp_ps + && version->major > shader_max_version_from_feature_level(shader->device->cs->c.state->feature_level)) { WARN("Shader version %u not supported by this device.\n", version->major); return WINED3DERR_INVALIDCALL; @@ -2549,6 +2550,18 @@ static void wined3d_shader_init_object(void *object) list_add_head(&device->shaders, &shader->shader_list_entry); + if (shader->is_ffp_ps) + { + struct ffp_frag_settings *settings = shader->byte_code; + struct wined3d_shader_desc desc; + + if (!ffp_hlsl_compile_ps(settings, &desc)) + return; + free(settings); + shader_set_function(shader, &desc, WINED3D_SHADER_TYPE_PIXEL, NULL, + device->adapter->d3d_info.limits.ps_uniform_count); + } + device->shader_backend->shader_precompile(device->shader_priv, shader); } @@ -3313,3 +3326,28 @@ HRESULT CDECL wined3d_shader_create_vs(struct wined3d_device *device, const stru return WINED3D_OK; } + +HRESULT wined3d_shader_create_ffp_ps(struct wined3d_device *device, + const struct ffp_frag_settings *settings, struct wined3d_shader **shader) +{ + struct wined3d_shader *object; + + if (!(object = calloc(1, sizeof(*object)))) + return E_OUTOFMEMORY; + + shader_init(object, device, NULL, &wined3d_null_parent_ops); + object->is_ffp_ps = true; + if (!(object->byte_code = malloc(sizeof(*settings)))) + { + free(object); + return E_OUTOFMEMORY; + } + memcpy(object->byte_code, settings, sizeof(*settings)); + + wined3d_cs_init_object(device->cs, wined3d_shader_init_object, object); + + TRACE("Created FFP pixel shader %p.\n", object); + *shader = object; + + return WINED3D_OK; +} diff --git a/dlls/wined3d/stateblock.c b/dlls/wined3d/stateblock.c index a79eb7b5f9f..fa5b3867d58 100644 --- a/dlls/wined3d/stateblock.c +++ b/dlls/wined3d/stateblock.c @@ -6,7 +6,7 @@ * Copyright 2005 Oliver Stieber * Copyright 2007 Stefan Dösinger for CodeWeavers * Copyright 2009 Henri Verbeet for CodeWeavers - * Copyright 2019,2020,2022 Zebediah Figura for CodeWeavers + * Copyright 2019,2020,2022-2024 Elizabeth Figura for CodeWeavers * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -1439,6 +1439,7 @@ void CDECL wined3d_stateblock_set_pixel_shader(struct wined3d_stateblock *stateb wined3d_shader_decref(stateblock->stateblock_state.ps); stateblock->stateblock_state.ps = shader; stateblock->changed.pixelShader = TRUE; + stateblock->changed.ffp_ps_settings = 1; } HRESULT CDECL wined3d_stateblock_set_ps_consts_f(struct wined3d_stateblock *stateblock, @@ -2379,10 +2380,10 @@ static void wined3d_stateblock_state_init(struct wined3d_stateblock_state *state } -/* FFP push constant buffers do not have a "default" state on the CS side. - * We need to explicitly invalidate them when initializing the context or - * resetting. */ -static void wined3d_stateblock_invalidate_push_constants(struct wined3d_stateblock *stateblock) +/* Some states, e.g. FFP push constant buffers, do not have a "default" state + * on the CS side. We need to explicitly invalidate them when initializing the + * context or resetting. */ +static void wined3d_stateblock_invalidate_initial_states(struct wined3d_stateblock *stateblock) { stateblock->changed.ffp_ps_constants = 1; stateblock->changed.lights = 1; @@ -2392,6 +2393,7 @@ static void wined3d_stateblock_invalidate_push_constants(struct wined3d_stateblo memset(stateblock->changed.transform, 0xff, sizeof(stateblock->changed.transform)); stateblock->changed.modelview_matrices = 1; stateblock->changed.point_scale = 1; + stateblock->changed.ffp_ps_settings = 1; } static HRESULT stateblock_init(struct wined3d_stateblock *stateblock, const struct wined3d_stateblock *device_state, @@ -2409,7 +2411,7 @@ static HRESULT stateblock_init(struct wined3d_stateblock *stateblock, const stru list_init(&stateblock->changed.changed_lights); if (type == WINED3D_SBT_PRIMARY) - wined3d_stateblock_invalidate_push_constants(stateblock); + wined3d_stateblock_invalidate_initial_states(stateblock); if (type == WINED3D_SBT_RECORDED || type == WINED3D_SBT_PRIMARY) return WINED3D_OK; @@ -2486,7 +2488,7 @@ void CDECL wined3d_stateblock_reset(struct wined3d_stateblock *stateblock) memset(&stateblock->stateblock_state, 0, sizeof(stateblock->stateblock_state)); stateblock->stateblock_state.light_state = &stateblock->light_state; wined3d_stateblock_state_init(&stateblock->stateblock_state, stateblock->device, WINED3D_STATE_INIT_DEFAULT); - wined3d_stateblock_invalidate_push_constants(stateblock); + wined3d_stateblock_invalidate_initial_states(stateblock); } static void wined3d_device_set_base_vertex_index(struct wined3d_device *device, int base_index) @@ -2909,6 +2911,31 @@ void CDECL wined3d_stateblock_apply_clear_state(struct wined3d_stateblock *state wined3d_device_set_render_state(device, WINED3D_RS_SRGBWRITEENABLE, state->rs[WINED3D_RS_SRGBWRITEENABLE]); } +static struct wined3d_shader *get_ffp_pixel_shader(struct wined3d_device *device, const struct wined3d_state *state) +{ + struct ffp_frag_settings settings; + const struct ffp_frag_desc *desc; + struct wined3d_ffp_ps *ps; + + wined3d_ffp_get_fs_settings(state, &device->adapter->d3d_info, &settings); + + if ((desc = find_ffp_frag_shader(&device->ffp_pixel_shaders, &settings))) + return CONTAINING_RECORD(desc, struct wined3d_ffp_ps, entry)->shader; + + if (!(ps = malloc(sizeof(*ps)))) + return NULL; + + ps->entry.settings = settings; + if (FAILED(wined3d_shader_create_ffp_ps(device, &settings, &ps->shader))) + { + free(ps); + return NULL; + } + add_ffp_frag_shader(&device->ffp_pixel_shaders, &ps->entry); + + return ps->shader; +} + void CDECL wined3d_device_apply_stateblock(struct wined3d_device *device, struct wined3d_stateblock *stateblock) { @@ -3746,6 +3773,11 @@ void CDECL wined3d_device_apply_stateblock(struct wined3d_device *device, WINED3D_SHADER_CONST_FFP_PS, 0, offsetof(struct wined3d_ffp_ps_constants, color_key), &constants); } + /* XXX: We don't invalidate HLSL shaders for every field contained in + * wined3d_ffp_vs_settings / ffp_frag_settings; only the ones that the HLSL + * FFP pipeline cares about. The rest should eventually be removed from + * those structs and left only in vs_compile_args / ps_compile_args. */ + if (changed->ffp_vs_settings && !state->vs) { /* Force invalidation of the vertex shader. */ @@ -3754,8 +3786,17 @@ void CDECL wined3d_device_apply_stateblock(struct wined3d_device *device, if (changed->ffp_ps_settings && !state->ps) { - /* Force invalidation of the pixel shader. */ - wined3d_device_context_emit_set_shader(context, WINED3D_SHADER_TYPE_PIXEL, NULL); + if (device->adapter->d3d_info.ffp_hlsl) + { + struct wined3d_shader *shader = get_ffp_pixel_shader(device, device->cs->c.state); + + wined3d_device_context_set_shader(context, WINED3D_SHADER_TYPE_PIXEL, shader); + } + else + { + /* Force invalidation of the pixel shader. */ + wined3d_device_context_emit_set_shader(context, WINED3D_SHADER_TYPE_PIXEL, NULL); + } } assert(list_empty(&stateblock->changed.changed_lights)); diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h index 50418992163..7b357218121 100644 --- a/dlls/wined3d/wined3d_private.h +++ b/dlls/wined3d/wined3d_private.h @@ -242,6 +242,7 @@ struct wined3d_d3d_info uint32_t fences : 1; uint32_t persistent_map : 1; uint32_t gpu_push_constants : 1; + uint32_t ffp_hlsl : 1; enum wined3d_feature_level feature_level; DWORD multisample_draw_location; @@ -2662,6 +2663,12 @@ struct ffp_frag_desc int wined3d_ffp_frag_program_key_compare(const void *key, const struct wine_rb_entry *entry); int wined3d_ffp_vertex_program_key_compare(const void *key, const struct wine_rb_entry *entry); +struct wined3d_ffp_ps +{ + struct ffp_frag_desc entry; + struct wined3d_shader *shader; +}; + extern const struct wined3d_parent_ops wined3d_null_parent_ops; void wined3d_ffp_get_fs_settings(const struct wined3d_state *state, @@ -2983,6 +2990,7 @@ struct wined3d_device struct list shaders; /* a linked list to track shaders (pixel and vertex) */ struct wine_rb_tree so_descs; struct wine_rb_tree samplers, rasterizer_states, blend_states, depth_stencil_states; + struct wine_rb_tree ffp_pixel_shaders; /* Render Target Support */ struct wined3d_rendertarget_view *auto_depth_stencil_view; @@ -4206,7 +4214,8 @@ struct wined3d_shader unsigned int functionLength; void *byte_code; unsigned int byte_code_size; - BOOL load_local_constsF; + bool load_local_constsF; + bool is_ffp_ps; enum vkd3d_shader_source_type source_type; const struct wined3d_shader_frontend *frontend; void *frontend_data; @@ -4244,6 +4253,9 @@ struct wined3d_shader } u; }; +HRESULT wined3d_shader_create_ffp_ps(struct wined3d_device *device, + const struct ffp_frag_settings *settings, struct wined3d_shader **shader); + enum wined3d_shader_resource_type pixelshader_get_resource_type(const struct wined3d_shader_reg_maps *reg_maps, unsigned int resource_idx, DWORD tex_types); void find_ps_compile_args(const struct wined3d_state *state, const struct wined3d_shader *shader, @@ -4273,6 +4285,8 @@ BOOL shader_match_semantic(const char *semantic_name, enum wined3d_decl_usage us enum vkd3d_shader_visibility vkd3d_shader_visibility_from_wined3d(enum wined3d_shader_type shader_type); +bool ffp_hlsl_compile_ps(const struct ffp_frag_settings *settings, struct wined3d_shader_desc *shader_desc); + static inline BOOL shader_is_scalar(const struct wined3d_shader_register *reg) { switch (reg->type)