wined3d: Do RGB <=> sRGB transfers using FBO blits.

Concept based on a patch by Stefan.
This commit is contained in:
Henri Verbeet 2010-07-26 12:06:32 +02:00 committed by Alexandre Julliard
parent fd6d9cd8f2
commit 943fb2fb57
6 changed files with 145 additions and 38 deletions

View File

@ -112,7 +112,7 @@ static void context_destroy_fbo(struct wined3d_context *context, GLuint *fbo)
}
/* GL locking is done by the caller */
static void context_apply_attachment_filter_states(IWineD3DSurfaceImpl *surface)
static void context_apply_attachment_filter_states(IWineD3DSurfaceImpl *surface, DWORD location)
{
IWineD3DBaseTextureImpl *texture_impl;
@ -123,18 +123,35 @@ static void context_apply_attachment_filter_states(IWineD3DSurfaceImpl *surface)
IWineD3DDeviceImpl *device = surface->resource.device;
BOOL update_minfilter = FALSE;
BOOL update_magfilter = FALSE;
struct gl_texture *gl_tex;
if (texture_impl->baseTexture.texture_rgb.states[WINED3DTEXSTA_MINFILTER] != WINED3DTEXF_POINT
|| texture_impl->baseTexture.texture_rgb.states[WINED3DTEXSTA_MIPFILTER] != WINED3DTEXF_NONE)
switch (location)
{
texture_impl->baseTexture.texture_rgb.states[WINED3DTEXSTA_MINFILTER] = WINED3DTEXF_POINT;
texture_impl->baseTexture.texture_rgb.states[WINED3DTEXSTA_MIPFILTER] = WINED3DTEXF_NONE;
case SFLAG_INTEXTURE:
gl_tex = &texture_impl->baseTexture.texture_rgb;
break;
case SFLAG_INSRGBTEX:
gl_tex = &texture_impl->baseTexture.texture_srgb;
break;
default:
ERR("Unsupported location %s (%#x).\n", debug_surflocation(location), location);
IWineD3DBaseTexture_Release((IWineD3DBaseTexture *)texture_impl);
return;
}
if (gl_tex->states[WINED3DTEXSTA_MINFILTER] != WINED3DTEXF_POINT
|| gl_tex->states[WINED3DTEXSTA_MIPFILTER] != WINED3DTEXF_NONE)
{
gl_tex->states[WINED3DTEXSTA_MINFILTER] = WINED3DTEXF_POINT;
gl_tex->states[WINED3DTEXSTA_MIPFILTER] = WINED3DTEXF_NONE;
update_minfilter = TRUE;
}
if (texture_impl->baseTexture.texture_rgb.states[WINED3DTEXSTA_MAGFILTER] != WINED3DTEXF_POINT)
if (gl_tex->states[WINED3DTEXSTA_MAGFILTER] != WINED3DTEXF_POINT)
{
texture_impl->baseTexture.texture_rgb.states[WINED3DTEXSTA_MAGFILTER] = WINED3DTEXF_POINT;
gl_tex->states[WINED3DTEXSTA_MAGFILTER] = WINED3DTEXF_POINT;
update_magfilter = TRUE;
}
@ -168,7 +185,7 @@ static void context_apply_attachment_filter_states(IWineD3DSurfaceImpl *surface)
glGetIntegerv(GL_TEXTURE_BINDING_CUBE_MAP_ARB, &old_binding);
}
glBindTexture(bind_target, surface->texture_name);
glBindTexture(bind_target, gl_tex->name);
if (update_minfilter) glTexParameteri(bind_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
if (update_magfilter) glTexParameteri(bind_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glBindTexture(bind_target, old_binding);
@ -209,7 +226,7 @@ void context_attach_depth_stencil_fbo(struct wined3d_context *context,
else
{
surface_prepare_texture(depth_stencil, gl_info, FALSE);
context_apply_attachment_filter_states(depth_stencil);
context_apply_attachment_filter_states(depth_stencil, SFLAG_INTEXTURE);
if (format_flags & WINED3DFMT_FLAG_DEPTH)
{
@ -252,7 +269,7 @@ void context_attach_depth_stencil_fbo(struct wined3d_context *context,
/* GL locking is done by the caller */
static void context_attach_surface_fbo(const struct wined3d_context *context,
GLenum fbo_target, DWORD idx, IWineD3DSurfaceImpl *surface)
GLenum fbo_target, DWORD idx, IWineD3DSurfaceImpl *surface, DWORD location)
{
const struct wined3d_gl_info *gl_info = context->gl_info;
@ -260,11 +277,26 @@ static void context_attach_surface_fbo(const struct wined3d_context *context,
if (surface)
{
surface_prepare_texture(surface, gl_info, FALSE);
context_apply_attachment_filter_states(surface);
switch (location)
{
case SFLAG_INTEXTURE:
surface_prepare_texture(surface, gl_info, FALSE);
context_apply_attachment_filter_states(surface, location);
gl_info->fbo_ops.glFramebufferTexture2D(fbo_target, GL_COLOR_ATTACHMENT0 + idx,
surface->texture_target, surface->texture_name, surface->texture_level);
break;
gl_info->fbo_ops.glFramebufferTexture2D(fbo_target, GL_COLOR_ATTACHMENT0 + idx, surface->texture_target,
surface->texture_name, surface->texture_level);
case SFLAG_INSRGBTEX:
surface_prepare_texture(surface, gl_info, TRUE);
context_apply_attachment_filter_states(surface, location);
gl_info->fbo_ops.glFramebufferTexture2D(fbo_target, GL_COLOR_ATTACHMENT0 + idx,
surface->texture_target, surface->texture_name_srgb, surface->texture_level);
break;
default:
ERR("Unsupported location %s (%#x).\n", debug_surflocation(location), location);
break;
}
checkGLcall("glFramebufferTexture2D()");
}
else
@ -295,6 +327,9 @@ static void context_check_fbo_status(struct wined3d_context *context, GLenum tar
return;
}
FIXME("\tLocation %s (%#x).\n", debug_surflocation(context->current_fbo->location),
context->current_fbo->location);
/* Dump the FBO attachments */
for (i = 0; i < gl_info->limits.buffers; ++i)
{
@ -317,7 +352,7 @@ static void context_check_fbo_status(struct wined3d_context *context, GLenum tar
}
static struct fbo_entry *context_create_fbo_entry(struct wined3d_context *context,
IWineD3DSurfaceImpl **render_targets, IWineD3DSurfaceImpl *depth_stencil)
IWineD3DSurfaceImpl **render_targets, IWineD3DSurfaceImpl *depth_stencil, DWORD location)
{
const struct wined3d_gl_info *gl_info = context->gl_info;
struct fbo_entry *entry;
@ -326,6 +361,7 @@ static struct fbo_entry *context_create_fbo_entry(struct wined3d_context *contex
entry->render_targets = HeapAlloc(GetProcessHeap(), 0, gl_info->limits.buffers * sizeof(*entry->render_targets));
memcpy(entry->render_targets, render_targets, gl_info->limits.buffers * sizeof(*entry->render_targets));
entry->depth_stencil = depth_stencil;
entry->location = location;
entry->attached = FALSE;
entry->id = 0;
@ -335,7 +371,7 @@ static struct fbo_entry *context_create_fbo_entry(struct wined3d_context *contex
/* GL locking is done by the caller */
static void context_reuse_fbo_entry(struct wined3d_context *context, GLenum target,
IWineD3DSurfaceImpl **render_targets, IWineD3DSurfaceImpl *depth_stencil,
struct fbo_entry *entry)
DWORD location, struct fbo_entry *entry)
{
const struct wined3d_gl_info *gl_info = context->gl_info;
@ -344,6 +380,7 @@ static void context_reuse_fbo_entry(struct wined3d_context *context, GLenum targ
memcpy(entry->render_targets, render_targets, gl_info->limits.buffers * sizeof(*entry->render_targets));
entry->depth_stencil = depth_stencil;
entry->location = location;
entry->attached = FALSE;
}
@ -364,7 +401,7 @@ static void context_destroy_fbo_entry(struct wined3d_context *context, struct fb
/* GL locking is done by the caller */
static struct fbo_entry *context_find_fbo_entry(struct wined3d_context *context, GLenum target,
IWineD3DSurfaceImpl **render_targets, IWineD3DSurfaceImpl *depth_stencil)
IWineD3DSurfaceImpl **render_targets, IWineD3DSurfaceImpl *depth_stencil, DWORD location)
{
const struct wined3d_gl_info *gl_info = context->gl_info;
struct fbo_entry *entry;
@ -373,7 +410,7 @@ static struct fbo_entry *context_find_fbo_entry(struct wined3d_context *context,
{
if (!memcmp(entry->render_targets,
render_targets, gl_info->limits.buffers * sizeof(*entry->render_targets))
&& entry->depth_stencil == depth_stencil)
&& entry->depth_stencil == depth_stencil && entry->location == location)
{
list_remove(&entry->entry);
list_add_head(&context->fbo_list, &entry->entry);
@ -383,14 +420,14 @@ static struct fbo_entry *context_find_fbo_entry(struct wined3d_context *context,
if (context->fbo_entry_count < WINED3D_MAX_FBO_ENTRIES)
{
entry = context_create_fbo_entry(context, render_targets, depth_stencil);
entry = context_create_fbo_entry(context, render_targets, depth_stencil, location);
list_add_head(&context->fbo_list, &entry->entry);
++context->fbo_entry_count;
}
else
{
entry = LIST_ENTRY(list_tail(&context->fbo_list), struct fbo_entry, entry);
context_reuse_fbo_entry(context, target, render_targets, depth_stencil, entry);
context_reuse_fbo_entry(context, target, render_targets, depth_stencil, location, entry);
list_remove(&entry->entry);
list_add_head(&context->fbo_list, &entry->entry);
}
@ -411,7 +448,7 @@ static void context_apply_fbo_entry(struct wined3d_context *context, GLenum targ
/* Apply render targets */
for (i = 0; i < gl_info->limits.buffers; ++i)
{
context_attach_surface_fbo(context, target, i, entry->render_targets[i]);
context_attach_surface_fbo(context, target, i, entry->render_targets[i], entry->location);
}
/* Apply depth targets */
@ -429,16 +466,16 @@ static void context_apply_fbo_entry(struct wined3d_context *context, GLenum targ
for (i = 0; i < gl_info->limits.buffers; ++i)
{
if (entry->render_targets[i])
context_apply_attachment_filter_states(entry->render_targets[i]);
context_apply_attachment_filter_states(entry->render_targets[i], entry->location);
}
if (entry->depth_stencil)
context_apply_attachment_filter_states(entry->depth_stencil);
context_apply_attachment_filter_states(entry->depth_stencil, SFLAG_INTEXTURE);
}
}
/* GL locking is done by the caller */
static void context_apply_fbo_state(struct wined3d_context *context, GLenum target,
IWineD3DSurfaceImpl **render_targets, IWineD3DSurfaceImpl *depth_stencil)
IWineD3DSurfaceImpl **render_targets, IWineD3DSurfaceImpl *depth_stencil, DWORD location)
{
struct fbo_entry *entry, *entry2;
@ -455,7 +492,7 @@ static void context_apply_fbo_state(struct wined3d_context *context, GLenum targ
if (render_targets)
{
context->current_fbo = context_find_fbo_entry(context, target, render_targets, depth_stencil);
context->current_fbo = context_find_fbo_entry(context, target, render_targets, depth_stencil, location);
context_apply_fbo_entry(context, target, context->current_fbo);
}
else
@ -469,18 +506,18 @@ static void context_apply_fbo_state(struct wined3d_context *context, GLenum targ
/* GL locking is done by the caller */
void context_apply_fbo_state_blit(struct wined3d_context *context, GLenum target,
IWineD3DSurfaceImpl *render_target, IWineD3DSurfaceImpl *depth_stencil)
IWineD3DSurfaceImpl *render_target, IWineD3DSurfaceImpl *depth_stencil, DWORD location)
{
if (surface_is_offscreen(render_target))
{
UINT clear_size = (context->gl_info->limits.buffers - 1) * sizeof(*context->blit_targets);
context->blit_targets[0] = render_target;
if (clear_size) memset(&context->blit_targets[1], 0, clear_size);
context_apply_fbo_state(context, target, context->blit_targets, depth_stencil);
context_apply_fbo_state(context, target, context->blit_targets, depth_stencil, location);
}
else
{
context_apply_fbo_state(context, target, NULL, NULL);
context_apply_fbo_state(context, target, NULL, NULL, location);
}
}
@ -2036,7 +2073,7 @@ void context_apply_blit_state(struct wined3d_context *context, IWineD3DDeviceImp
surface_internal_preload(context->current_rt, SRGB_RGB);
ENTER_GL();
context_apply_fbo_state_blit(context, GL_FRAMEBUFFER, context->current_rt, NULL);
context_apply_fbo_state_blit(context, GL_FRAMEBUFFER, context->current_rt, NULL, SFLAG_INTEXTURE);
LEAVE_GL();
}
else
@ -2083,11 +2120,11 @@ void context_apply_clear_state(struct wined3d_context *context, IWineD3DDeviceIm
context->blit_targets[i] = NULL;
++i;
}
context_apply_fbo_state(context, GL_FRAMEBUFFER, context->blit_targets, depth_stencil);
context_apply_fbo_state(context, GL_FRAMEBUFFER, context->blit_targets, depth_stencil, SFLAG_INTEXTURE);
}
else
{
context_apply_fbo_state(context, GL_FRAMEBUFFER, NULL, NULL);
context_apply_fbo_state(context, GL_FRAMEBUFFER, NULL, NULL, SFLAG_INDRAWABLE);
}
LEAVE_GL();
@ -2161,13 +2198,14 @@ void context_apply_draw_state(struct wined3d_context *context, IWineD3DDeviceImp
if (!context->render_offscreen)
{
ENTER_GL();
context_apply_fbo_state(context, GL_FRAMEBUFFER, NULL, NULL);
context_apply_fbo_state(context, GL_FRAMEBUFFER, NULL, NULL, SFLAG_INDRAWABLE);
LEAVE_GL();
}
else
{
ENTER_GL();
context_apply_fbo_state(context, GL_FRAMEBUFFER, device->render_targets, device->depth_stencil);
context_apply_fbo_state(context, GL_FRAMEBUFFER, device->render_targets,
device->depth_stencil, SFLAG_INTEXTURE);
LEAVE_GL();
}
}

View File

@ -5821,7 +5821,7 @@ void stretch_rect_fbo(IWineD3DDeviceImpl *device, IWineD3DSurfaceImpl *src_surfa
} else {
TRACE("Source surface %p is offscreen\n", src_surface);
ENTER_GL();
context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER, src_surface, NULL);
context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER, src_surface, NULL, SFLAG_INTEXTURE);
glReadBuffer(GL_COLOR_ATTACHMENT0);
checkGLcall("glReadBuffer()");
}
@ -5849,7 +5849,7 @@ void stretch_rect_fbo(IWineD3DDeviceImpl *device, IWineD3DSurfaceImpl *src_surfa
TRACE("Destination surface %p is offscreen\n", dst_surface);
ENTER_GL();
context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER, dst_surface, NULL);
context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER, dst_surface, NULL, SFLAG_INTEXTURE);
context_set_draw_buffer(context, GL_COLOR_ATTACHMENT0);
}

View File

@ -4206,6 +4206,43 @@ static inline void surface_blt_to_drawable(IWineD3DSurfaceImpl *This, const RECT
context_release(context);
}
static void surface_load_srgb_fbo(IWineD3DSurfaceImpl *surface, DWORD location)
{
IWineD3DDeviceImpl *device = surface->resource.device;
const struct wined3d_gl_info *gl_info;
struct wined3d_context *context;
context = context_acquire(device, NULL);
gl_info = context->gl_info;
ENTER_GL();
context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER, surface, NULL,
location == SFLAG_INSRGBTEX ? SFLAG_INTEXTURE : SFLAG_INSRGBTEX);
glReadBuffer(GL_COLOR_ATTACHMENT0);
checkGLcall("glReadBuffer()");
context_apply_fbo_state_blit(context, GL_DRAW_FRAMEBUFFER, surface, NULL, location);
context_set_draw_buffer(context, GL_COLOR_ATTACHMENT0);
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
IWineD3DDeviceImpl_MarkStateDirty(device, STATE_RENDER(WINED3DRS_COLORWRITEENABLE));
IWineD3DDeviceImpl_MarkStateDirty(device, STATE_RENDER(WINED3DRS_COLORWRITEENABLE1));
IWineD3DDeviceImpl_MarkStateDirty(device, STATE_RENDER(WINED3DRS_COLORWRITEENABLE2));
IWineD3DDeviceImpl_MarkStateDirty(device, STATE_RENDER(WINED3DRS_COLORWRITEENABLE3));
glDisable(GL_SCISSOR_TEST);
IWineD3DDeviceImpl_MarkStateDirty(device, STATE_RENDER(WINED3DRS_SCISSORTESTENABLE));
gl_info->fbo_ops.glBlitFramebuffer(0, 0, surface->currentDesc.Width, surface->currentDesc.Height,
0, 0, surface->currentDesc.Width, surface->currentDesc.Height, GL_COLOR_BUFFER_BIT, GL_NEAREST);
checkGLcall("glBlitFramebuffer()");
LEAVE_GL();
context_release(context);
}
HRESULT surface_load_location(IWineD3DSurfaceImpl *surface, DWORD flag, const RECT *rect)
{
IWineD3DDeviceImpl *device = surface->resource.device;
@ -4360,10 +4397,20 @@ HRESULT surface_load_location(IWineD3DSurfaceImpl *surface, DWORD flag, const RE
}
else /* if(flag & (SFLAG_INTEXTURE | SFLAG_INSRGBTEX)) */
{
const DWORD attach_flags = WINED3DFMT_FLAG_FBO_ATTACHABLE | WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB;
if (drawable_read_ok && (surface->Flags & SFLAG_INDRAWABLE))
{
read_from_framebuffer_texture(surface, flag == SFLAG_INSRGBTEX);
}
else if (surface->Flags & (SFLAG_INSRGBTEX | SFLAG_INTEXTURE)
&& (surface->resource.format_desc->Flags & attach_flags) == attach_flags
&& fbo_blit_supported(gl_info, BLIT_OP_BLIT,
NULL, surface->resource.usage, surface->resource.pool, surface->resource.format_desc,
NULL, surface->resource.usage, surface->resource.pool, surface->resource.format_desc))
{
surface_load_srgb_fbo(surface, flag);
}
else
{
/* Upload from system memory */

View File

@ -120,7 +120,7 @@ static void swapchain_blit(IWineD3DSwapChainImpl *This, struct wined3d_context *
if (gl_info->fbo_ops.glBlitFramebuffer && is_identity_fixup(backbuffer->resource.format_desc->color_fixup))
{
ENTER_GL();
context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER, backbuffer, NULL);
context_apply_fbo_state_blit(context, GL_READ_FRAMEBUFFER, backbuffer, NULL, SFLAG_INTEXTURE);
glReadBuffer(GL_COLOR_ATTACHMENT0);
context_bind_fbo(context, GL_DRAW_FRAMEBUFFER, NULL);

View File

@ -1056,6 +1056,26 @@ static void check_fbo_compat(const struct wined3d_gl_info *gl_info, struct wined
}
}
if (format_desc->glInternal != format_desc->glGammaInternal)
{
glTexImage2D(GL_TEXTURE_2D, 0, format_desc->glGammaInternal, 16, 16, 0,
format_desc->glFormat, format_desc->glType, NULL);
gl_info->fbo_ops.glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex, 0);
status = gl_info->fbo_ops.glCheckFramebufferStatus(GL_FRAMEBUFFER);
checkGLcall("Framebuffer format check");
if (status == GL_FRAMEBUFFER_COMPLETE)
{
TRACE("Format %s's sRGB format is FBO attachable.\n", debug_d3dformat(format_desc->format));
format_desc->Flags |= WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB;
}
else
{
WARN("Format %s's sRGB format is not FBO attachable.\n", debug_d3dformat(format_desc->format));
}
}
glDeleteTextures(1, &tex);
LEAVE_GL();

View File

@ -1164,7 +1164,7 @@ void context_apply_clear_state(struct wined3d_context *context, IWineD3DDeviceIm
UINT rt_count, IWineD3DSurfaceImpl **rts, IWineD3DSurfaceImpl *depth_stencil) DECLSPEC_HIDDEN;
void context_apply_draw_state(struct wined3d_context *context, IWineD3DDeviceImpl *device) DECLSPEC_HIDDEN;
void context_apply_fbo_state_blit(struct wined3d_context *context, GLenum target,
IWineD3DSurfaceImpl *render_target, IWineD3DSurfaceImpl *depth_stencil) DECLSPEC_HIDDEN;
IWineD3DSurfaceImpl *render_target, IWineD3DSurfaceImpl *depth_stencil, DWORD location) DECLSPEC_HIDDEN;
void context_attach_depth_stencil_fbo(struct wined3d_context *context,
GLenum fbo_target, IWineD3DSurfaceImpl *depth_stencil, BOOL use_render_buffer) DECLSPEC_HIDDEN;
void context_bind_fbo(struct wined3d_context *context, GLenum target, GLuint *fbo) DECLSPEC_HIDDEN;
@ -1979,6 +1979,7 @@ struct fbo_entry
struct list entry;
IWineD3DSurfaceImpl **render_targets;
IWineD3DSurfaceImpl *depth_stencil;
DWORD location;
BOOL attached;
GLuint id;
};
@ -2929,7 +2930,7 @@ extern WINED3DFORMAT pixelformat_for_depth(DWORD depth) DECLSPEC_HIDDEN;
#define WINED3DFMT_FLAG_RENDERTARGET 0x00000010
#define WINED3DFMT_FLAG_FOURCC 0x00000020
#define WINED3DFMT_FLAG_FBO_ATTACHABLE 0x00000040
#define WINED3DFMT_FLAG_COMPRESSED 0x00000080
#define WINED3DFMT_FLAG_FBO_ATTACHABLE_SRGB 0x00000080
#define WINED3DFMT_FLAG_GETDC 0x00000100
#define WINED3DFMT_FLAG_FLOAT 0x00000200
#define WINED3DFMT_FLAG_BUMPMAP 0x00000400
@ -2937,6 +2938,7 @@ extern WINED3DFORMAT pixelformat_for_depth(DWORD depth) DECLSPEC_HIDDEN;
#define WINED3DFMT_FLAG_SRGB_WRITE 0x00001000
#define WINED3DFMT_FLAG_VTF 0x00002000
#define WINED3DFMT_FLAG_SHADOW 0x00004000
#define WINED3DFMT_FLAG_COMPRESSED 0x00008000
struct wined3d_format_desc
{