Module: wine Branch: refs/heads/master Commit: b67cda248dcad7790b3da084623d1d59d96c7a07 URL: http://source.winehq.org/git/?p=wine.git;a=commit;h=b67cda248dcad7790b3da084...
Author: Jason Green jave27@gmail.com Date: Fri Jun 9 03:36:55 2006 -0400
wined3d: Add GLSL helper functions to Device.
- Add functions to attach & detach shader objects, create and delete programs, and maintain the list of programs. - Add a list of GLSL shader programs to the device which is initialized on Init3D(), and deleted on Release().
---
dlls/wined3d/device.c | 187 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 187 insertions(+), 0 deletions(-)
diff --git a/dlls/wined3d/device.c b/dlls/wined3d/device.c index 13d497c..067a871 100644 --- a/dlls/wined3d/device.c +++ b/dlls/wined3d/device.c @@ -24,12 +24,14 @@ */
#include "config.h" +#include <stdio.h> #ifdef HAVE_FLOAT_H # include <float.h> #endif #include "wined3d_private.h"
WINE_DEFAULT_DEBUG_CHANNEL(d3d); +WINE_DECLARE_DEBUG_CHANNEL(d3d_shader); #define GLINFO_LOCATION ((IWineD3DImpl *)(This->wineD3D))->gl_info
/* Define the default light parameters as specified by MSDN */ @@ -238,6 +240,183 @@ static void setup_light(IWineD3DDevice * glPopMatrix(); }
+/********************************************************** + * GLSL helper functions follow + **********************************************************/ + +/** Attach a GLSL pixel or vertex shader object to the shader program */ +static void attach_glsl_shader(IWineD3DDevice *iface, IWineD3DBaseShader* shader) { + + IWineD3DDeviceImpl *This = (IWineD3DDeviceImpl *)iface; + GLhandleARB shaderObj = ((IWineD3DBaseShaderImpl*)shader)->baseShader.prgId; + if (This->stateBlock->shaderPrgId != 0 && shaderObj != 0) { + TRACE_(d3d_shader)("Attaching GLSL shader object %u to program %u\n", shaderObj, This->stateBlock->shaderPrgId); + GL_EXTCALL(glAttachObjectARB(This->stateBlock->shaderPrgId, shaderObj)); + checkGLcall("glAttachObjectARB"); + } +} + +/** Sets the GLSL program ID for the given pixel and vertex shader combination. + * It sets the programId on the current StateBlock (because it should be called + * inside of the DrawPrimitive() part of the render loop). + * + * If a program for the given combination does not exist, create one, and store + * the program in the list. If it creates a program, it will link the given + * objects, too. + * + * We keep the shader programs around on a list because linking + * shader objects together is an expensive operation. It's much + * faster to loop through a list of pre-compiled & linked programs + * each time that the application sets a new pixel or vertex shader + * than it is to re-link them together at that time. + * + * The list will be deleted in IWineD3DDevice::Release(). + */ +void set_glsl_shader_program(IWineD3DDevice *iface) { + + IWineD3DDeviceImpl *This = (IWineD3DDeviceImpl *)iface; + IWineD3DPixelShader *pshader = This->stateBlock->pixelShader; + IWineD3DVertexShader *vshader = This->stateBlock->vertexShader; + struct glsl_shader_prog_link *curLink = NULL; + struct glsl_shader_prog_link *newLink = NULL; + struct list *ptr = NULL; + GLhandleARB programId = 0; + + if (NULL == vshader && NULL == pshader) { + /* No pixel or vertex shader specified */ + This->stateBlock->shaderPrgId = 0; + return; + } + + ptr = list_head( &This->glsl_shader_progs ); + while (ptr) { + /* At least one program exists - see if it matches our ps/vs combination */ + curLink = LIST_ENTRY( ptr, struct glsl_shader_prog_link, entry ); + if (vshader == curLink->vertexShader && pshader == curLink->pixelShader) { + /* Existing Program found, use it */ + TRACE_(d3d_shader)("Found existing program (%u) for this vertex/pixel shader combination\n", + curLink->programId); + This->stateBlock->shaderPrgId = curLink->programId; + return; + } + /* This isn't the entry we need - try the next one */ + ptr = list_next( &This->glsl_shader_progs, ptr ); + } + + /* If we get to this point, then no matching program exists, so we create one */ + programId = GL_EXTCALL(glCreateProgramObjectARB()); + TRACE_(d3d_shader)("Created new GLSL shader program %u\n", programId); + This->stateBlock->shaderPrgId = programId; + + if (NULL != vshader) { + int i; + int max_attribs = 16; /* TODO: Will this always be the case? It is at the moment... */ + char tmp_name[10]; + + attach_glsl_shader(iface, (IWineD3DBaseShader*)vshader); + + /* Bind vertex attributes to a corresponding index number to match + * the same index numbers as ARB_vertex_programs (makes loading + * vertex attributes simpler). With this method, we can use the + * exact same code to load the attributes later for both ARB and + * GLSL shaders. + * + * We have to do this here because we need to know the Program ID + * in order to make the bindings work, and it has to be done prior + * to linking the GLSL program. */ + for (i = 0; i < max_attribs; ++i) { + snprintf(tmp_name, sizeof(tmp_name), "attrib%i", i); + GL_EXTCALL(glBindAttribLocationARB(programId, i, tmp_name)); + } + checkGLcall("glBindAttribLocationARB"); + } + + if (NULL != pshader) { + attach_glsl_shader(iface, (IWineD3DBaseShader*)pshader); + } + + /* Link the program */ + TRACE_(d3d_shader)("Linking GLSL shader program %u\n", programId); + GL_EXTCALL(glLinkProgramARB(programId)); + print_glsl_info_log(&GLINFO_LOCATION, programId); + + /* Now, we add a list item to associate this program with the vertex and + * pixel shaders that it is attached to. + * + * These list items will be deleted when the device is released. + */ + newLink = HeapAlloc(GetProcessHeap(), 0, sizeof(struct glsl_shader_prog_link)); + newLink->programId = programId; + newLink->pixelShader = pshader; + newLink->vertexShader = vshader; + list_add_head( &This->glsl_shader_progs, &newLink->entry); + + return; +} + +/** Detach the GLSL pixel or vertex shader object from the shader program */ +static void detach_glsl_shader(IWineD3DDevice *iface, GLhandleARB shaderObj, GLhandleARB programId) { + + IWineD3DDeviceImpl *This = (IWineD3DDeviceImpl *)iface; + + if (shaderObj != 0 && programId != 0) { + TRACE_(d3d_shader)("Detaching GLSL shader object %u from program %u\n", shaderObj, programId); + GL_EXTCALL(glDetachObjectARB(programId, shaderObj)); + checkGLcall("glDetachObjectARB"); + } +} + +/** Delete a GLSL shader program */ +static void delete_glsl_shader_program(IWineD3DDevice *iface, GLhandleARB obj) { + + IWineD3DDeviceImpl *This = (IWineD3DDeviceImpl *)iface; + + if (obj != 0) { + TRACE_(d3d_shader)("Deleting GLSL shader program %u\n", obj); + GL_EXTCALL(glDeleteObjectARB(obj)); + checkGLcall("glDeleteObjectARB"); + } +} + +/** Delete the list of linked programs this shader is associated with. + * Also at this point, check to see if there are any objects left attached + * to each GLSL program. If not, delete the GLSL program object. + * This will be run when a device is released. */ +static void delete_glsl_shader_list(IWineD3DDevice* iface) { + + struct list *ptr = NULL; + struct glsl_shader_prog_link *curLink = NULL; + IWineD3DDeviceImpl *This = (IWineD3DDeviceImpl *)iface; + + int numAttached = 0; + int i; + GLhandleARB objList[2]; /* There should never be more than 2 objects attached + (one pixel shader and one vertex shader at most) */ + + ptr = list_head( &This->glsl_shader_progs ); + while (ptr) { + /* First, get the current item, + * save the link to the next pointer, + * detach and delete shader objects, + * then de-allocate the list item's memory */ + curLink = LIST_ENTRY( ptr, struct glsl_shader_prog_link, entry ); + ptr = list_next( &This->glsl_shader_progs, ptr ); + + /* See if this object is still attached to the program - it may have been detached already */ + GL_EXTCALL(glGetAttachedObjectsARB(curLink->programId, 2, &numAttached, objList)); + TRACE_(d3d_shader)("%i GLSL objects are currently attached to program %u\n", numAttached, curLink->programId); + for (i = 0; i < numAttached; i++) { + detach_glsl_shader(iface, objList[i], curLink->programId); + } + + delete_glsl_shader_program(iface, curLink->programId); + + /* Free the memory for this list item */ + HeapFree(GetProcessHeap(), 0, curLink); + } +} + + /* Apply the current values to the specified texture stage */ void WINAPI IWineD3DDeviceImpl_SetupTextureStates(IWineD3DDevice *iface, DWORD Sampler, DWORD Flags) { IWineD3DDeviceImpl *This = (IWineD3DDeviceImpl *)iface; @@ -387,6 +566,11 @@ ULONG WINAPI IWineD3DDeviceImpl_Release( /* NOTE: You must release the parent if the object was created via a callback ** ***************************/
+ /* Delete any GLSL shader programs that may exist */ + if (wined3d_settings.shader_mode == SHADER_GLSL) { + delete_glsl_shader_list(iface); + } + /* Release the update stateblock */ if(IWineD3DStateBlock_Release((IWineD3DStateBlock *)This->updateStateBlock) > 0){ if(This->updateStateBlock != This->stateBlock) @@ -1737,6 +1921,9 @@ #if 0 #endif LEAVE_GL();
+ /* Initialize our list of GLSL programs */ + list_init(&This->glsl_shader_progs); + { /* Set a default viewport */ D3DVIEWPORT9 vp; vp.X = 0;