wine/dlls/wined3d/context.c
Zebediah Figura 5b60f4649b wined3d: Set d3d 1-9 textures in the state as SRVs.
The idea is to reuse the existing code to handle SRVs, which simplifies the GL
code and essentially allows the Vulkan code to work "for free" (which is to say,
by writing this patch, rather than by adding support for flat textures to the
Vulkan renderer.)

This is a large patch; it consists the following parts:

* Create identity SRVs for d3d 1-9 textures. Store those in
  state->shader_resource_view instead of in state->texture.

* (Re)use wined3d_context_gl_bind_shader_resources() instead of state_sampler()
  to bind them.

  - Introduce code to that function to handle FFP textures.

  - Bind the sRGB texture if necessary in wined3d_shader_resource_view_gl_bind.

* (Re)use context_gl_load_shader_resources() instead of
  context_preload_textures() to load them.

  - Introduce code to that function to handle FFP textures.

  - Load the sRGB texture if necessary.

  - Port the SRV/RTV feedback loop check from context_preload_textures().

* Invalidate STATE_GRAPHICS_SHADER_RESOURCE_BINDING in places that now need to
  account for texture binding being guarded by that state instead of
  STATE_SAMPLER.

  Transitioning the remaining users of STATE_SAMPLER to
  STATE_GRAPHICS_SHADER_RESOURCE_BINDING, and removing STATE_SAMPLER, is left
  for future patches.
2023-12-01 18:46:23 +01:00

336 lines
13 KiB
C

/*
* Context and render target management in wined3d
*
* Copyright 2002-2004 Jason Edmeades
* Copyright 2002-2004 Raphael Junqueira
* Copyright 2004 Christian Costa
* Copyright 2005 Oliver Stieber
* Copyright 2006, 2008 Henri Verbeet
* Copyright 2007-2011, 2013 Stefan Dösinger for CodeWeavers
* Copyright 2009-2011 Henri Verbeet 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"
WINE_DEFAULT_DEBUG_CHANNEL(d3d);
WINE_DECLARE_DEBUG_CHANNEL(d3d_perf);
void context_resource_released(const struct wined3d_device *device, struct wined3d_resource *resource)
{
unsigned int i;
for (i = 0; i < device->context_count; ++i)
{
struct wined3d_context *context = device->contexts[i];
if (&context->current_rt.texture->resource == resource)
{
context->current_rt.texture = NULL;
context->current_rt.sub_resource_idx = 0;
}
}
}
void wined3d_context_cleanup(struct wined3d_context *context)
{
}
/* This is used when a context for render target A is active, but a separate context is
* needed to access the WGL framebuffer for render target B. Re-acquire a context for rt
* A to avoid breaking caller code. */
void context_restore(struct wined3d_context *context, struct wined3d_texture *texture, unsigned int sub_resource_idx)
{
if (context->current_rt.texture != texture || context->current_rt.sub_resource_idx != sub_resource_idx)
{
context_release(context);
context = context_acquire(texture->resource.device, texture, sub_resource_idx);
}
context_release(context);
}
void context_invalidate_compute_state(struct wined3d_context *context, DWORD state_id)
{
DWORD representative = context->state_table[state_id].representative - STATE_COMPUTE_OFFSET;
unsigned int index, shift;
index = representative / (sizeof(*context->dirty_compute_states) * CHAR_BIT);
shift = representative & (sizeof(*context->dirty_compute_states) * CHAR_BIT - 1);
context->dirty_compute_states[index] |= (1u << shift);
}
void context_invalidate_state(struct wined3d_context *context, unsigned int state_id)
{
unsigned int representative = context->state_table[state_id].representative;
unsigned int index, shift;
if (!representative)
ERR("Invalidating representative 0, state_id %u.\n", state_id);
index = representative / (sizeof(*context->dirty_graphics_states) * CHAR_BIT);
shift = representative & ((sizeof(*context->dirty_graphics_states) * CHAR_BIT) - 1);
context->dirty_graphics_states[index] |= (1u << shift);
}
void wined3d_context_init(struct wined3d_context *context, struct wined3d_swapchain *swapchain)
{
struct wined3d_device *device = swapchain->device;
DWORD state;
context->d3d_info = &device->adapter->d3d_info;
context->state_table = device->state_table;
/* Mark all states dirty to force a proper initialization of the states on
* the first use of the context. Compute states do not need initialization. */
for (state = 0; state <= STATE_HIGHEST; ++state)
{
if (context->state_table[state].representative && !STATE_IS_COMPUTE(state))
context_invalidate_state(context, state);
}
context->device = device;
context->swapchain = swapchain;
context->current_rt.texture = swapchain->front_buffer;
context->current_rt.sub_resource_idx = 0;
context->shader_update_mask = (1u << WINED3D_SHADER_TYPE_PIXEL)
| (1u << WINED3D_SHADER_TYPE_VERTEX)
| (1u << WINED3D_SHADER_TYPE_GEOMETRY)
| (1u << WINED3D_SHADER_TYPE_HULL)
| (1u << WINED3D_SHADER_TYPE_DOMAIN)
| (1u << WINED3D_SHADER_TYPE_COMPUTE);
}
HRESULT wined3d_context_no3d_init(struct wined3d_context *context_no3d, struct wined3d_swapchain *swapchain)
{
TRACE("context_no3d %p, swapchain %p.\n", context_no3d, swapchain);
wined3d_context_init(context_no3d, swapchain);
return WINED3D_OK;
}
static BOOL fixed_get_input(BYTE usage, BYTE usage_idx, unsigned int *regnum)
{
if ((usage == WINED3D_DECL_USAGE_POSITION || usage == WINED3D_DECL_USAGE_POSITIONT) && !usage_idx)
*regnum = WINED3D_FFP_POSITION;
else if (usage == WINED3D_DECL_USAGE_BLEND_WEIGHT && !usage_idx)
*regnum = WINED3D_FFP_BLENDWEIGHT;
else if (usage == WINED3D_DECL_USAGE_BLEND_INDICES && !usage_idx)
*regnum = WINED3D_FFP_BLENDINDICES;
else if (usage == WINED3D_DECL_USAGE_NORMAL && !usage_idx)
*regnum = WINED3D_FFP_NORMAL;
else if (usage == WINED3D_DECL_USAGE_PSIZE && !usage_idx)
*regnum = WINED3D_FFP_PSIZE;
else if (usage == WINED3D_DECL_USAGE_COLOR && !usage_idx)
*regnum = WINED3D_FFP_DIFFUSE;
else if (usage == WINED3D_DECL_USAGE_COLOR && usage_idx == 1)
*regnum = WINED3D_FFP_SPECULAR;
else if (usage == WINED3D_DECL_USAGE_TEXCOORD && usage_idx < WINED3DDP_MAXTEXCOORD)
*regnum = WINED3D_FFP_TEXCOORD0 + usage_idx;
else
{
WARN("Unsupported input stream [usage=%s, usage_idx=%u].\n", debug_d3ddeclusage(usage), usage_idx);
*regnum = ~0u;
return FALSE;
}
return TRUE;
}
/* Context activation is done by the caller. */
void wined3d_stream_info_from_declaration(struct wined3d_stream_info *stream_info,
const struct wined3d_state *state, const struct wined3d_d3d_info *d3d_info)
{
/* We need to deal with frequency data! */
struct wined3d_vertex_declaration *declaration = state->vertex_declaration;
BOOL generic_attributes = d3d_info->ffp_generic_attributes;
BOOL use_vshader = use_vs(state);
unsigned int i;
stream_info->use_map = 0;
stream_info->swizzle_map = 0;
stream_info->position_transformed = 0;
if (!declaration)
return;
stream_info->position_transformed = declaration->position_transformed;
/* Translate the declaration into strided data. */
for (i = 0; i < declaration->element_count; ++i)
{
const struct wined3d_vertex_declaration_element *element = &declaration->elements[i];
const struct wined3d_stream_state *stream = &state->streams[element->input_slot];
BOOL stride_used;
unsigned int idx;
TRACE("%p Element %p (%u of %u).\n", declaration->elements,
element, i + 1, declaration->element_count);
if (!stream->buffer)
continue;
TRACE("offset %u input_slot %u usage_idx %d.\n", element->offset, element->input_slot, element->usage_idx);
if (use_vshader)
{
if (element->output_slot == WINED3D_OUTPUT_SLOT_UNUSED)
{
stride_used = FALSE;
}
else if (element->output_slot == WINED3D_OUTPUT_SLOT_SEMANTIC)
{
/* TODO: Assuming vertexdeclarations are usually used with the
* same or a similar shader, it might be worth it to store the
* last used output slot and try that one first. */
stride_used = vshader_get_input(state->shader[WINED3D_SHADER_TYPE_VERTEX],
element->usage, element->usage_idx, &idx);
}
else
{
idx = element->output_slot;
stride_used = TRUE;
}
}
else
{
if (!generic_attributes && !element->ffp_valid)
{
WARN("Skipping unsupported fixed function element of format %s and usage %s.\n",
debug_d3dformat(element->format->id), debug_d3ddeclusage(element->usage));
stride_used = FALSE;
}
else
{
stride_used = fixed_get_input(element->usage, element->usage_idx, &idx);
}
}
if (stride_used)
{
TRACE("Load %s array %u [usage %s, usage_idx %u, "
"input_slot %u, offset %u, stride %u, format %s, class %s, step_rate %u].\n",
use_vshader ? "shader": "fixed function", idx,
debug_d3ddeclusage(element->usage), element->usage_idx, element->input_slot,
element->offset, stream->stride, debug_d3dformat(element->format->id),
debug_d3dinput_classification(element->input_slot_class), element->instance_data_step_rate);
stream_info->elements[idx].format = element->format;
stream_info->elements[idx].data.buffer_object = 0;
stream_info->elements[idx].data.addr = (BYTE *)NULL + stream->offset + element->offset;
stream_info->elements[idx].stride = stream->stride;
stream_info->elements[idx].stream_idx = element->input_slot;
if (stream->flags & WINED3DSTREAMSOURCE_INSTANCEDATA)
{
stream_info->elements[idx].divisor = 1;
stream_info->elements[idx].instanced = true;
}
else if (element->input_slot_class == WINED3D_INPUT_PER_INSTANCE_DATA)
{
stream_info->elements[idx].divisor = element->instance_data_step_rate;
stream_info->elements[idx].instanced = true;
}
else
{
stream_info->elements[idx].divisor = 0;
stream_info->elements[idx].instanced = false;
}
if (!d3d_info->vertex_bgra && element->format->id == WINED3DFMT_B8G8R8A8_UNORM)
{
stream_info->swizzle_map |= 1u << idx;
}
stream_info->use_map |= 1u << idx;
}
}
}
/* Context activation is done by the caller. */
void context_update_stream_info(struct wined3d_context *context, const struct wined3d_state *state)
{
struct wined3d_stream_info *stream_info = &context->stream_info;
const struct wined3d_d3d_info *d3d_info = context->d3d_info;
DWORD prev_all_vbo = stream_info->all_vbo;
unsigned int i;
uint32_t map;
wined3d_stream_info_from_declaration(stream_info, state, d3d_info);
stream_info->all_vbo = 1;
map = stream_info->use_map;
while (map)
{
struct wined3d_stream_info_element *element;
struct wined3d_bo_address data;
struct wined3d_buffer *buffer;
i = wined3d_bit_scan(&map);
element = &stream_info->elements[i];
buffer = state->streams[element->stream_idx].buffer;
/* We can't use VBOs if the base vertex index is negative. OpenGL
* doesn't accept negative offsets (or rather offsets bigger than the
* VBO, because the pointer is unsigned), so use system memory
* sources. In most sane cases the pointer - offset will still be > 0,
* otherwise it will wrap around to some big value. Hope that with the
* indices the driver wraps it back internally. If not,
* draw_primitive_immediate_mode() is needed, including a vertex buffer
* path. */
if (state->load_base_vertex_index < 0)
{
WARN_(d3d_perf)("load_base_vertex_index is < 0 (%d), not using VBOs.\n",
state->load_base_vertex_index);
element->data.buffer_object = 0;
element->data.addr += (ULONG_PTR)wined3d_buffer_load_sysmem(buffer, context);
if ((UINT_PTR)element->data.addr < -state->load_base_vertex_index * element->stride)
FIXME("System memory vertex data load offset is negative!\n");
}
else
{
wined3d_buffer_load(buffer, context, state);
wined3d_buffer_get_memory(buffer, context, &data);
element->data.buffer_object = data.buffer_object;
element->data.addr += (ULONG_PTR)data.addr;
}
if (!element->data.buffer_object)
stream_info->all_vbo = 0;
TRACE("Load array %u %s.\n", i, debug_bo_address(&element->data));
}
if (prev_all_vbo != stream_info->all_vbo)
context_invalidate_state(context, STATE_INDEXBUFFER);
context->use_immediate_mode_draw = FALSE;
if (stream_info->all_vbo)
return;
if (!use_vs(state))
{
WORD slow_mask = -!d3d_info->ffp_generic_attributes & (1u << WINED3D_FFP_PSIZE);
slow_mask |= -(!d3d_info->vertex_bgra && !d3d_info->ffp_generic_attributes)
& ((1u << WINED3D_FFP_DIFFUSE) | (1u << WINED3D_FFP_SPECULAR) | (1u << WINED3D_FFP_BLENDWEIGHT));
if ((stream_info->position_transformed && !d3d_info->xyzrhw)
|| (stream_info->use_map & slow_mask))
context->use_immediate_mode_draw = TRUE;
}
}