2003-11-14 04:50:35 +01:00
|
|
|
/*
|
|
|
|
* shaders implementation
|
|
|
|
*
|
|
|
|
* Copyright 2002-2003 Jason Edmeades
|
2005-08-17 11:54:54 +02:00
|
|
|
* Copyright 2002-2003 Raphael Junqueira
|
2006-07-17 20:35:14 +02:00
|
|
|
* Copyright 2004 Christian Costa
|
2005-08-17 11:54:54 +02:00
|
|
|
* Copyright 2005 Oliver Stieber
|
2006-05-17 08:04:30 +02:00
|
|
|
* Copyright 2006 Ivan Gyurdiev
|
2008-10-18 19:21:20 +02:00
|
|
|
* Copyright 2007-2008 Stefan Dösinger for CodeWeavers
|
2003-11-14 04:50:35 +01:00
|
|
|
*
|
|
|
|
* 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
|
2006-05-18 14:49:52 +02:00
|
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
2003-11-14 04:50:35 +01:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include "config.h"
|
|
|
|
|
|
|
|
#include <math.h>
|
2005-08-17 13:34:03 +02:00
|
|
|
#include <stdio.h>
|
2003-11-14 04:50:35 +01:00
|
|
|
|
|
|
|
#include "wined3d_private.h"
|
|
|
|
|
|
|
|
WINE_DEFAULT_DEBUG_CHANNEL(d3d_shader);
|
|
|
|
|
2007-06-09 14:27:41 +02:00
|
|
|
#define GLINFO_LOCATION ((IWineD3DDeviceImpl *)This->baseShader.device)->adapter->gl_info
|
2005-08-17 13:34:03 +02:00
|
|
|
|
2009-05-06 10:05:45 +02:00
|
|
|
static void vshader_set_limits(IWineD3DVertexShaderImpl *This)
|
|
|
|
{
|
2009-05-06 17:59:21 +02:00
|
|
|
DWORD shader_version = WINED3D_SHADER_VERSION(This->baseShader.reg_maps.shader_version.major,
|
|
|
|
This->baseShader.reg_maps.shader_version.minor);
|
|
|
|
|
2009-05-06 10:05:45 +02:00
|
|
|
This->baseShader.limits.texcoord = 0;
|
|
|
|
This->baseShader.limits.attributes = 16;
|
|
|
|
This->baseShader.limits.packed_input = 0;
|
2006-05-08 23:09:21 +02:00
|
|
|
|
2009-05-06 17:59:21 +02:00
|
|
|
switch (shader_version)
|
2009-05-06 10:05:45 +02:00
|
|
|
{
|
2009-05-06 17:59:21 +02:00
|
|
|
case WINED3D_SHADER_VERSION(1,0):
|
|
|
|
case WINED3D_SHADER_VERSION(1,1):
|
2009-05-06 10:05:45 +02:00
|
|
|
This->baseShader.limits.temporary = 12;
|
|
|
|
This->baseShader.limits.constant_bool = 0;
|
|
|
|
This->baseShader.limits.constant_int = 0;
|
|
|
|
This->baseShader.limits.address = 1;
|
|
|
|
This->baseShader.limits.packed_output = 0;
|
|
|
|
This->baseShader.limits.sampler = 0;
|
|
|
|
This->baseShader.limits.label = 0;
|
|
|
|
/* TODO: vs_1_1 has a minimum of 96 constants. What happens if a vs_1_1 shader is used
|
|
|
|
* on a vs_3_0 capable card that has 256 constants? */
|
|
|
|
This->baseShader.limits.constant_float = min(256, GL_LIMITS(vshader_constantsF));
|
|
|
|
break;
|
2006-05-08 23:09:21 +02:00
|
|
|
|
2009-05-06 17:59:21 +02:00
|
|
|
case WINED3D_SHADER_VERSION(2,0):
|
|
|
|
case WINED3D_SHADER_VERSION(2,1):
|
2009-05-06 10:05:45 +02:00
|
|
|
This->baseShader.limits.temporary = 12;
|
|
|
|
This->baseShader.limits.constant_bool = 16;
|
|
|
|
This->baseShader.limits.constant_int = 16;
|
|
|
|
This->baseShader.limits.address = 1;
|
|
|
|
This->baseShader.limits.packed_output = 0;
|
|
|
|
This->baseShader.limits.sampler = 0;
|
|
|
|
This->baseShader.limits.label = 16;
|
|
|
|
This->baseShader.limits.constant_float = min(256, GL_LIMITS(vshader_constantsF));
|
|
|
|
break;
|
2009-04-25 15:02:42 +02:00
|
|
|
|
2009-05-06 17:59:21 +02:00
|
|
|
case WINED3D_SHADER_VERSION(3,0):
|
2009-05-06 10:05:45 +02:00
|
|
|
This->baseShader.limits.temporary = 32;
|
|
|
|
This->baseShader.limits.constant_bool = 32;
|
|
|
|
This->baseShader.limits.constant_int = 32;
|
|
|
|
This->baseShader.limits.address = 1;
|
|
|
|
This->baseShader.limits.packed_output = 12;
|
|
|
|
This->baseShader.limits.sampler = 4;
|
|
|
|
This->baseShader.limits.label = 16; /* FIXME: 2048 */
|
|
|
|
/* DX10 cards on Windows advertise a d3d9 constant limit of 256 even though they are capable
|
|
|
|
* of supporting much more(GL drivers advertise 1024). d3d9.dll and d3d8.dll clamp the
|
|
|
|
* wined3d-advertised maximum. Clamp the constant limit for <= 3.0 shaders to 256.s
|
|
|
|
* use constant buffers */
|
|
|
|
This->baseShader.limits.constant_float = min(256, GL_LIMITS(vshader_constantsF));
|
|
|
|
break;
|
2006-05-08 23:09:21 +02:00
|
|
|
|
2009-05-06 10:05:45 +02:00
|
|
|
default:
|
|
|
|
This->baseShader.limits.temporary = 12;
|
|
|
|
This->baseShader.limits.constant_bool = 16;
|
|
|
|
This->baseShader.limits.constant_int = 16;
|
|
|
|
This->baseShader.limits.address = 1;
|
|
|
|
This->baseShader.limits.packed_output = 0;
|
|
|
|
This->baseShader.limits.sampler = 0;
|
|
|
|
This->baseShader.limits.label = 16;
|
|
|
|
This->baseShader.limits.constant_float = min(256, GL_LIMITS(vshader_constantsF));
|
2009-05-06 17:59:21 +02:00
|
|
|
FIXME("Unrecognized vertex shader version %u.%u\n",
|
|
|
|
This->baseShader.reg_maps.shader_version.major,
|
|
|
|
This->baseShader.reg_maps.shader_version.minor);
|
2009-05-06 10:05:45 +02:00
|
|
|
}
|
2006-05-08 23:09:21 +02:00
|
|
|
}
|
|
|
|
|
2006-07-07 08:28:30 +02:00
|
|
|
/* This is an internal function,
|
|
|
|
* used to create fake semantics for shaders
|
|
|
|
* that don't have them - d3d8 shaders where the declaration
|
|
|
|
* stores the register for each input
|
|
|
|
*/
|
|
|
|
static void vshader_set_input(
|
|
|
|
IWineD3DVertexShaderImpl* This,
|
|
|
|
unsigned int regnum,
|
|
|
|
BYTE usage, BYTE usage_idx) {
|
|
|
|
|
2009-04-08 08:35:07 +02:00
|
|
|
This->semantics_in[regnum].usage = usage;
|
|
|
|
This->semantics_in[regnum].usage_idx = usage_idx;
|
2009-05-07 16:36:07 +02:00
|
|
|
This->semantics_in[regnum].reg.reg.type = WINED3DSPR_INPUT;
|
|
|
|
This->semantics_in[regnum].reg.reg.idx = regnum;
|
2009-04-08 08:35:06 +02:00
|
|
|
This->semantics_in[regnum].reg.write_mask = WINED3DSP_WRITEMASK_ALL;
|
|
|
|
This->semantics_in[regnum].reg.modifiers = 0;
|
2009-04-10 09:15:06 +02:00
|
|
|
This->semantics_in[regnum].reg.shift = 0;
|
2009-05-07 16:36:07 +02:00
|
|
|
This->semantics_in[regnum].reg.reg.rel_addr = NULL;
|
2006-07-07 08:28:30 +02:00
|
|
|
}
|
|
|
|
|
2007-02-27 20:51:18 +01:00
|
|
|
static BOOL match_usage(BYTE usage1, BYTE usage_idx1, BYTE usage2, BYTE usage_idx2) {
|
|
|
|
if (usage_idx1 != usage_idx2) return FALSE;
|
|
|
|
if (usage1 == usage2) return TRUE;
|
|
|
|
if (usage1 == WINED3DDECLUSAGE_POSITION && usage2 == WINED3DDECLUSAGE_POSITIONT) return TRUE;
|
|
|
|
if (usage2 == WINED3DDECLUSAGE_POSITION && usage1 == WINED3DDECLUSAGE_POSITIONT) return TRUE;
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2006-07-07 08:27:38 +02:00
|
|
|
BOOL vshader_get_input(
|
|
|
|
IWineD3DVertexShader* iface,
|
|
|
|
BYTE usage_req, BYTE usage_idx_req,
|
|
|
|
unsigned int* regnum) {
|
|
|
|
|
|
|
|
IWineD3DVertexShaderImpl* This = (IWineD3DVertexShaderImpl*) iface;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < MAX_ATTRIBS; i++) {
|
2009-04-08 08:35:07 +02:00
|
|
|
if (!This->baseShader.reg_maps.attributes[i]) continue;
|
|
|
|
|
2009-04-08 08:35:07 +02:00
|
|
|
if (match_usage(This->semantics_in[i].usage,
|
|
|
|
This->semantics_in[i].usage_idx, usage_req, usage_idx_req))
|
2009-04-08 08:35:07 +02:00
|
|
|
{
|
2006-07-07 08:27:38 +02:00
|
|
|
*regnum = i;
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2005-03-02 13:16:10 +01:00
|
|
|
/* *******************************************
|
|
|
|
IWineD3DVertexShader IUnknown parts follow
|
|
|
|
******************************************* */
|
2007-11-16 21:01:33 +01:00
|
|
|
static HRESULT WINAPI IWineD3DVertexShaderImpl_QueryInterface(IWineD3DVertexShader *iface, REFIID riid, LPVOID *ppobj) {
|
2008-12-09 09:52:39 +01:00
|
|
|
TRACE("iface %p, riid %s, ppobj %p\n", iface, debugstr_guid(riid), ppobj);
|
|
|
|
|
|
|
|
if (IsEqualGUID(riid, &IID_IWineD3DVertexShader)
|
|
|
|
|| IsEqualGUID(riid, &IID_IWineD3DBaseShader)
|
|
|
|
|| IsEqualGUID(riid, &IID_IWineD3DBase)
|
|
|
|
|| IsEqualGUID(riid, &IID_IUnknown))
|
|
|
|
{
|
|
|
|
IUnknown_AddRef(iface);
|
|
|
|
*ppobj = iface;
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
WARN("%s not implemented, returning E_NOINTERFACE\n", debugstr_guid(riid));
|
|
|
|
|
|
|
|
*ppobj = NULL;
|
|
|
|
return E_NOINTERFACE;
|
2005-03-02 13:16:10 +01:00
|
|
|
}
|
|
|
|
|
2007-11-16 21:01:33 +01:00
|
|
|
static ULONG WINAPI IWineD3DVertexShaderImpl_AddRef(IWineD3DVertexShader *iface) {
|
2008-12-09 09:52:39 +01:00
|
|
|
IWineD3DVertexShaderImpl *This = (IWineD3DVertexShaderImpl *)iface;
|
|
|
|
ULONG refcount = InterlockedIncrement(&This->baseShader.ref);
|
|
|
|
|
|
|
|
TRACE("%p increasing refcount to %u\n", This, refcount);
|
|
|
|
|
|
|
|
return refcount;
|
2005-03-02 13:16:10 +01:00
|
|
|
}
|
|
|
|
|
2006-06-10 13:15:32 +02:00
|
|
|
static ULONG WINAPI IWineD3DVertexShaderImpl_Release(IWineD3DVertexShader *iface) {
|
2008-12-09 09:52:39 +01:00
|
|
|
IWineD3DVertexShaderImpl *This = (IWineD3DVertexShaderImpl *)iface;
|
|
|
|
ULONG refcount = InterlockedDecrement(&This->baseShader.ref);
|
|
|
|
|
|
|
|
TRACE("%p decreasing refcount to %u\n", This, refcount);
|
|
|
|
|
|
|
|
if (!refcount)
|
|
|
|
{
|
|
|
|
shader_cleanup((IWineD3DBaseShader *)iface);
|
|
|
|
HeapFree(GetProcessHeap(), 0, This);
|
|
|
|
}
|
|
|
|
|
|
|
|
return refcount;
|
2005-03-02 13:16:10 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* *******************************************
|
|
|
|
IWineD3DVertexShader IWineD3DVertexShader parts follow
|
|
|
|
******************************************* */
|
|
|
|
|
2006-06-10 13:15:32 +02:00
|
|
|
static HRESULT WINAPI IWineD3DVertexShaderImpl_GetParent(IWineD3DVertexShader *iface, IUnknown** parent){
|
2005-03-02 13:16:10 +01:00
|
|
|
IWineD3DVertexShaderImpl *This = (IWineD3DVertexShaderImpl *)iface;
|
2005-08-19 12:05:00 +02:00
|
|
|
|
2005-12-03 18:10:56 +01:00
|
|
|
*parent = This->parent;
|
2005-03-02 13:16:10 +01:00
|
|
|
IUnknown_AddRef(*parent);
|
|
|
|
TRACE("(%p) : returning %p\n", This, *parent);
|
2006-04-07 12:51:12 +02:00
|
|
|
return WINED3D_OK;
|
2005-03-02 13:16:10 +01:00
|
|
|
}
|
2005-07-13 16:15:54 +02:00
|
|
|
|
2006-06-10 13:15:32 +02:00
|
|
|
static HRESULT WINAPI IWineD3DVertexShaderImpl_GetDevice(IWineD3DVertexShader* iface, IWineD3DDevice **pDevice){
|
2005-03-02 13:16:10 +01:00
|
|
|
IWineD3DVertexShaderImpl *This = (IWineD3DVertexShaderImpl *)iface;
|
2006-09-27 13:14:46 +02:00
|
|
|
IWineD3DDevice_AddRef(This->baseShader.device);
|
|
|
|
*pDevice = This->baseShader.device;
|
2005-07-13 16:15:54 +02:00
|
|
|
TRACE("(%p) returning %p\n", This, *pDevice);
|
2006-04-07 12:51:12 +02:00
|
|
|
return WINED3D_OK;
|
2005-03-02 13:16:10 +01:00
|
|
|
}
|
|
|
|
|
2006-06-10 13:15:32 +02:00
|
|
|
static HRESULT WINAPI IWineD3DVertexShaderImpl_GetFunction(IWineD3DVertexShader* impl, VOID* pData, UINT* pSizeOfData) {
|
2005-08-17 11:54:54 +02:00
|
|
|
IWineD3DVertexShaderImpl *This = (IWineD3DVertexShaderImpl *)impl;
|
2006-06-09 23:46:38 +02:00
|
|
|
TRACE("(%p) : pData(%p), pSizeOfData(%p)\n", This, pData, pSizeOfData);
|
2005-03-02 13:16:10 +01:00
|
|
|
|
2005-08-17 11:54:54 +02:00
|
|
|
if (NULL == pData) {
|
2006-03-30 19:14:31 +02:00
|
|
|
*pSizeOfData = This->baseShader.functionLength;
|
2006-04-07 12:51:12 +02:00
|
|
|
return WINED3D_OK;
|
2005-08-17 11:54:54 +02:00
|
|
|
}
|
2006-03-30 19:14:31 +02:00
|
|
|
if (*pSizeOfData < This->baseShader.functionLength) {
|
2007-03-08 01:16:09 +01:00
|
|
|
/* MSDN claims (for d3d8 at least) that if *pSizeOfData is smaller
|
|
|
|
* than the required size we should write the required size and
|
|
|
|
* return D3DERR_MOREDATA. That's not actually true. */
|
|
|
|
return WINED3DERR_INVALIDCALL;
|
2005-08-17 11:54:54 +02:00
|
|
|
}
|
2008-12-15 16:35:14 +01:00
|
|
|
|
|
|
|
TRACE("(%p) : GetFunction copying to %p\n", This, pData);
|
|
|
|
memcpy(pData, This->baseShader.function, This->baseShader.functionLength);
|
|
|
|
|
2006-04-07 12:51:12 +02:00
|
|
|
return WINED3D_OK;
|
2005-03-02 13:16:10 +01:00
|
|
|
}
|
|
|
|
|
2006-08-05 18:15:35 +02:00
|
|
|
/* Note that for vertex shaders CompileShader isn't called until the
|
|
|
|
* shader is first used. The reason for this is that we need the vertex
|
|
|
|
* declaration the shader will be used with in order to determine if
|
|
|
|
* the data in a register is of type D3DCOLOR, and needs swizzling. */
|
2009-05-08 17:44:25 +02:00
|
|
|
static HRESULT WINAPI IWineD3DVertexShaderImpl_SetFunction(IWineD3DVertexShader *iface,
|
|
|
|
const DWORD *pFunction, const struct wined3d_shader_signature *output_signature)
|
|
|
|
{
|
2006-06-12 08:54:30 +02:00
|
|
|
IWineD3DVertexShaderImpl *This =(IWineD3DVertexShaderImpl *)iface;
|
2006-09-27 13:14:46 +02:00
|
|
|
IWineD3DDeviceImpl *deviceImpl = (IWineD3DDeviceImpl *) This->baseShader.device;
|
2009-05-04 09:49:27 +02:00
|
|
|
const struct wined3d_shader_frontend *fe;
|
2006-07-10 06:51:03 +02:00
|
|
|
HRESULT hr;
|
2006-08-05 18:15:35 +02:00
|
|
|
shader_reg_maps *reg_maps = &This->baseShader.reg_maps;
|
2006-05-17 08:04:30 +02:00
|
|
|
|
2006-07-25 00:51:03 +02:00
|
|
|
TRACE("(%p) : pFunction %p\n", iface, pFunction);
|
|
|
|
|
2009-05-04 09:49:27 +02:00
|
|
|
fe = shader_select_frontend(*pFunction);
|
|
|
|
if (!fe)
|
|
|
|
{
|
|
|
|
FIXME("Unable to find frontend for shader.\n");
|
|
|
|
return WINED3DERR_INVALIDCALL;
|
|
|
|
}
|
2009-05-05 09:38:03 +02:00
|
|
|
This->baseShader.frontend = fe;
|
2009-05-08 17:44:25 +02:00
|
|
|
This->baseShader.frontend_data = fe->shader_init(pFunction, output_signature);
|
2009-05-05 09:38:03 +02:00
|
|
|
if (!This->baseShader.frontend_data)
|
|
|
|
{
|
|
|
|
FIXME("Failed to initialize frontend.\n");
|
|
|
|
return WINED3DERR_INVALIDCALL;
|
|
|
|
}
|
2009-05-04 09:49:27 +02:00
|
|
|
|
2006-07-04 10:01:46 +02:00
|
|
|
/* First pass: trace shader */
|
2009-05-05 09:38:03 +02:00
|
|
|
if (TRACE_ON(d3d_shader)) shader_trace_init(fe, This->baseShader.frontend_data, pFunction);
|
2005-03-02 13:16:10 +01:00
|
|
|
|
2006-07-10 06:51:03 +02:00
|
|
|
/* Initialize immediate constant lists */
|
|
|
|
list_init(&This->baseShader.constantsF);
|
|
|
|
list_init(&This->baseShader.constantsB);
|
|
|
|
list_init(&This->baseShader.constantsI);
|
|
|
|
|
2006-07-04 10:01:46 +02:00
|
|
|
/* Second pass: figure out registers used, semantics, etc.. */
|
2007-11-07 19:57:49 +01:00
|
|
|
This->min_rel_offset = GL_LIMITS(vshader_constantsF);
|
|
|
|
This->max_rel_offset = 0;
|
2009-05-04 09:49:27 +02:00
|
|
|
hr = shader_get_registers_used((IWineD3DBaseShader*) This, fe,
|
2009-05-07 17:31:20 +02:00
|
|
|
reg_maps, This->semantics_in, This->semantics_out, pFunction,
|
|
|
|
GL_LIMITS(vshader_constantsF));
|
2006-07-10 06:51:03 +02:00
|
|
|
if (hr != WINED3D_OK) return hr;
|
2006-07-04 10:01:46 +02:00
|
|
|
|
2008-12-11 11:52:37 +01:00
|
|
|
vshader_set_limits(This);
|
|
|
|
|
2007-11-07 19:57:49 +01:00
|
|
|
if(deviceImpl->vs_selected_mode == SHADER_ARB &&
|
|
|
|
(GLINFO_LOCATION).arb_vs_offset_limit &&
|
|
|
|
This->min_rel_offset <= This->max_rel_offset) {
|
|
|
|
|
|
|
|
if(This->max_rel_offset - This->min_rel_offset > 127) {
|
|
|
|
FIXME("The difference between the minimum and maximum relative offset is > 127\n");
|
|
|
|
FIXME("Which this OpenGL implementation does not support. Try using GLSL\n");
|
|
|
|
FIXME("Min: %d, Max: %d\n", This->min_rel_offset, This->max_rel_offset);
|
|
|
|
} else if(This->max_rel_offset - This->min_rel_offset > 63) {
|
|
|
|
This->rel_offset = This->min_rel_offset + 63;
|
|
|
|
} else if(This->max_rel_offset > 63) {
|
|
|
|
This->rel_offset = This->min_rel_offset;
|
|
|
|
} else {
|
|
|
|
This->rel_offset = 0;
|
|
|
|
}
|
|
|
|
}
|
2007-11-09 14:48:47 +01:00
|
|
|
This->baseShader.load_local_constsF = This->baseShader.reg_maps.usesrelconstF && !list_empty(&This->baseShader.constantsF);
|
2007-11-07 19:57:49 +01:00
|
|
|
|
2005-08-17 13:34:03 +02:00
|
|
|
/* copy the function ... because it will certainly be released by application */
|
2008-12-15 16:35:14 +01:00
|
|
|
This->baseShader.function = HeapAlloc(GetProcessHeap(), 0, This->baseShader.functionLength);
|
|
|
|
if (!This->baseShader.function) return E_OUTOFMEMORY;
|
|
|
|
memcpy(This->baseShader.function, pFunction, This->baseShader.functionLength);
|
wined3d: Rework shader mode selection.
- currently half the shader selection code (GLSL vs ARB) is in
fillGLcaps. The parts that check for software shaders are in
GetDeviceCaps. That placement, will work, but is definitely not optimal.
FillGLcaps should detect support - it should not make decision as to
what's used, because that's not what the purpose of the function is.
GetDeviceCaps should report support as it has already been selected.
Instead, select shader mode in its own function, called in the
appropriate places.
- unifying pixel and vertex shaders into a single selection is a
mistake. A software vertex shader can be coupled with a hardware arb or
glsl pixel shader, or no shader at all. Split them back into two and add
a SHADER_NONE variant.
- drawprim is doing support checks for ARB_PROGRAM, and making shader
decisions based on that - that's wrong, support has already been
checked, and decided upon, and shaders can be implemented via software,
ARB_PROGRAm or GLSL, so that support check isn't valid.
- Store the shader selected mode into the shader itself. Different types
of shaders can be combined, so this is an improvement. In fact, storing
the mode into the settings globally is a mistake as well - it should be
done per device, since different cards have different capabilities.
2006-07-04 09:21:53 +02:00
|
|
|
|
2006-04-07 12:51:12 +02:00
|
|
|
return WINED3D_OK;
|
2005-08-17 13:34:03 +02:00
|
|
|
}
|
2005-03-02 13:16:10 +01:00
|
|
|
|
2007-02-13 23:12:29 +01:00
|
|
|
/* Preload semantics for d3d8 shaders */
|
|
|
|
static void WINAPI IWineD3DVertexShaderImpl_FakeSemantics(IWineD3DVertexShader *iface, IWineD3DVertexDeclaration *vertex_declaration) {
|
|
|
|
IWineD3DVertexShaderImpl *This =(IWineD3DVertexShaderImpl *)iface;
|
|
|
|
IWineD3DVertexDeclarationImpl* vdecl = (IWineD3DVertexDeclarationImpl*)vertex_declaration;
|
|
|
|
|
2009-03-25 10:12:27 +01:00
|
|
|
unsigned int i;
|
2009-03-27 10:25:56 +01:00
|
|
|
for (i = 0; i < vdecl->element_count; ++i)
|
|
|
|
{
|
|
|
|
const struct wined3d_vertex_declaration_element *e = &vdecl->elements[i];
|
|
|
|
vshader_set_input(This, e->output_slot, e->usage, e->usage_idx);
|
2007-02-13 23:12:29 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-02-13 23:12:40 +01:00
|
|
|
/* Set local constants for d3d8 shaders */
|
|
|
|
static HRESULT WINAPI IWIneD3DVertexShaderImpl_SetLocalConstantsF(IWineD3DVertexShader *iface,
|
|
|
|
UINT start_idx, const float *src_data, UINT count) {
|
|
|
|
IWineD3DVertexShaderImpl *This =(IWineD3DVertexShaderImpl *)iface;
|
|
|
|
UINT i, end_idx;
|
|
|
|
|
|
|
|
TRACE("(%p) : start_idx %u, src_data %p, count %u\n", This, start_idx, src_data, count);
|
|
|
|
|
|
|
|
end_idx = start_idx + count;
|
|
|
|
if (end_idx > GL_LIMITS(vshader_constantsF)) {
|
|
|
|
WARN("end_idx %u > float constants limit %u\n", end_idx, GL_LIMITS(vshader_constantsF));
|
|
|
|
end_idx = GL_LIMITS(vshader_constantsF);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = start_idx; i < end_idx; ++i) {
|
|
|
|
local_constant* lconst = HeapAlloc(GetProcessHeap(), 0, sizeof(local_constant));
|
|
|
|
if (!lconst) return E_OUTOFMEMORY;
|
|
|
|
|
|
|
|
lconst->idx = i;
|
2007-06-16 11:44:08 +02:00
|
|
|
memcpy(lconst->value, src_data + (i - start_idx) * 4 /* 4 components */, 4 * sizeof(float));
|
2007-02-13 23:12:40 +01:00
|
|
|
list_add_head(&This->baseShader.constantsF, &lconst->entry);
|
|
|
|
}
|
|
|
|
|
2007-02-14 23:30:40 +01:00
|
|
|
return WINED3D_OK;
|
2007-02-13 23:12:40 +01:00
|
|
|
}
|
|
|
|
|
2009-02-05 19:44:32 +01:00
|
|
|
static GLuint vertexshader_compile(IWineD3DVertexShaderImpl *This, const struct vs_compile_args *args) {
|
2007-11-20 21:14:10 +01:00
|
|
|
IWineD3DDeviceImpl *deviceImpl = (IWineD3DDeviceImpl *) This->baseShader.device;
|
2009-02-05 19:44:32 +01:00
|
|
|
SHADER_BUFFER buffer;
|
|
|
|
GLuint ret;
|
2006-08-05 18:15:35 +02:00
|
|
|
|
|
|
|
/* Generate the HW shader */
|
|
|
|
TRACE("(%p) : Generating hardware program\n", This);
|
2009-02-05 19:44:32 +01:00
|
|
|
shader_buffer_init(&buffer);
|
|
|
|
This->cur_args = args;
|
2009-05-05 09:38:03 +02:00
|
|
|
ret = deviceImpl->shader_backend->shader_generate_vshader((IWineD3DVertexShader *)This, &buffer, args);
|
2009-02-05 19:44:32 +01:00
|
|
|
This->cur_args = NULL;
|
|
|
|
shader_buffer_free(&buffer);
|
2006-08-05 18:15:35 +02:00
|
|
|
|
2009-02-05 19:44:32 +01:00
|
|
|
return ret;
|
2006-08-05 18:15:35 +02:00
|
|
|
}
|
|
|
|
|
2005-06-06 21:50:35 +02:00
|
|
|
const IWineD3DVertexShaderVtbl IWineD3DVertexShader_Vtbl =
|
2005-03-02 13:16:10 +01:00
|
|
|
{
|
|
|
|
/*** IUnknown methods ***/
|
|
|
|
IWineD3DVertexShaderImpl_QueryInterface,
|
|
|
|
IWineD3DVertexShaderImpl_AddRef,
|
|
|
|
IWineD3DVertexShaderImpl_Release,
|
2006-03-28 21:10:51 +02:00
|
|
|
/*** IWineD3DBase methods ***/
|
2005-03-02 13:16:10 +01:00
|
|
|
IWineD3DVertexShaderImpl_GetParent,
|
2006-03-28 21:10:51 +02:00
|
|
|
/*** IWineD3DBaseShader methods ***/
|
|
|
|
IWineD3DVertexShaderImpl_SetFunction,
|
|
|
|
/*** IWineD3DVertexShader methods ***/
|
2005-03-02 13:16:10 +01:00
|
|
|
IWineD3DVertexShaderImpl_GetDevice,
|
2007-02-13 23:12:29 +01:00
|
|
|
IWineD3DVertexShaderImpl_GetFunction,
|
2007-02-13 23:12:40 +01:00
|
|
|
IWineD3DVertexShaderImpl_FakeSemantics,
|
|
|
|
IWIneD3DVertexShaderImpl_SetLocalConstantsF
|
2005-03-02 13:16:10 +01:00
|
|
|
};
|
2009-02-05 19:44:32 +01:00
|
|
|
|
|
|
|
void find_vs_compile_args(IWineD3DVertexShaderImpl *shader, IWineD3DStateBlockImpl *stateblock, struct vs_compile_args *args) {
|
|
|
|
args->fog_src = stateblock->renderState[WINED3DRS_FOGTABLEMODE] == WINED3DFOG_NONE ? VS_FOG_COORD : VS_FOG_Z;
|
|
|
|
args->swizzle_map = ((IWineD3DDeviceImpl *)shader->baseShader.device)->strided_streams.swizzle_map;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline BOOL vs_args_equal(const struct vs_compile_args *stored, const struct vs_compile_args *new,
|
|
|
|
const DWORD use_map) {
|
|
|
|
if((stored->swizzle_map & use_map) != new->swizzle_map) return FALSE;
|
|
|
|
return stored->fog_src == new->fog_src;
|
|
|
|
}
|
|
|
|
|
|
|
|
GLuint find_gl_vshader(IWineD3DVertexShaderImpl *shader, const struct vs_compile_args *args)
|
|
|
|
{
|
|
|
|
UINT i;
|
|
|
|
DWORD new_size = shader->shader_array_size;
|
|
|
|
struct vs_compiled_shader *new_array;
|
|
|
|
DWORD use_map = ((IWineD3DDeviceImpl *)shader->baseShader.device)->strided_streams.use_map;
|
|
|
|
|
|
|
|
/* Usually we have very few GL shaders for each d3d shader(just 1 or maybe 2),
|
|
|
|
* so a linear search is more performant than a hashmap or a binary search
|
|
|
|
* (cache coherency etc)
|
|
|
|
*/
|
|
|
|
for(i = 0; i < shader->num_gl_shaders; i++) {
|
|
|
|
if(vs_args_equal(&shader->gl_shaders[i].args, args, use_map)) {
|
|
|
|
return shader->gl_shaders[i].prgId;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
TRACE("No matching GL shader found, compiling a new shader\n");
|
|
|
|
|
|
|
|
if(shader->shader_array_size == shader->num_gl_shaders) {
|
2009-03-24 10:09:21 +01:00
|
|
|
if (shader->num_gl_shaders)
|
|
|
|
{
|
2009-02-05 19:44:32 +01:00
|
|
|
new_size = shader->shader_array_size + max(1, shader->shader_array_size / 2);
|
|
|
|
new_array = HeapReAlloc(GetProcessHeap(), 0, shader->gl_shaders,
|
|
|
|
new_size * sizeof(*shader->gl_shaders));
|
|
|
|
} else {
|
|
|
|
new_array = HeapAlloc(GetProcessHeap(), 0, sizeof(*shader->gl_shaders));
|
|
|
|
new_size = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!new_array) {
|
|
|
|
ERR("Out of memory\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
shader->gl_shaders = new_array;
|
|
|
|
shader->shader_array_size = new_size;
|
|
|
|
}
|
|
|
|
|
|
|
|
shader->gl_shaders[shader->num_gl_shaders].args = *args;
|
|
|
|
shader->gl_shaders[shader->num_gl_shaders].prgId = vertexshader_compile(shader, args);
|
|
|
|
return shader->gl_shaders[shader->num_gl_shaders++].prgId;
|
|
|
|
}
|