Show More
Commit Description:
Dialog tweaks and update swear list....
Commit Description:
Dialog tweaks and update swear list. Keep the list of swears to censor up to date, lol.
File last commit:
Show/Diff file:
Action:
FNA/lib/FNA3D/MojoShader/mojoshader_d3d11.c
1018 lines | 31.3 KiB | text/x-c | CLexer
/**
* MojoShader; generate shader programs from bytecode of compiled
* Direct3D shaders.
*
* Please see the file LICENSE.txt in the source's root directory.
*
* This file written by Ryan C. Gordon.
*/
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN 1
#include <windows.h> // Include this early to avoid SDL conflicts
#endif
#define __MOJOSHADER_INTERNAL__ 1
#include "mojoshader_internal.h"
#if SUPPORT_PROFILE_HLSL
#define D3D11_NO_HELPERS
#define CINTERFACE
#define COBJMACROS
#include <d3d11.h>
#ifndef WINAPI_FAMILY_WINRT
#define WINAPI_FAMILY_WINRT 0
#endif
#if WINAPI_FAMILY_WINRT
#include <d3dcompiler.h>
#elif defined(_WIN32)
#define LOAD_D3DCOMPILER LoadLibrary("d3dcompiler_47.dll")
#define UNLOAD_D3DCOMPILER(d) FreeLibrary(d)
#define LOAD_D3DCOMPILE(d) GetProcAddress(d, "D3DCompile")
#else
#if defined(__APPLE__)
#define LOAD_D3DCOMPILER dlopen("libvkd3d-utils.1.dylib", RTLD_NOW|RTLD_LOCAL)
#else
#define LOAD_D3DCOMPILER dlopen("libvkd3d-utils.so.1", RTLD_NOW|RTLD_LOCAL)
#endif
#define UNLOAD_D3DCOMPILER(d) dlclose(d)
#define LOAD_D3DCOMPILE(d) dlsym(d, "D3DCompile")
#endif
// D3DCompile optimization can be overzealous and cause very visible bugs,
// so we disable it when compiling shaders to preserve correctness.
#define D3D_SKIP_OPT (1 << 2)
/* Error state */
static char error_buffer[1024] = { '\0' };
static void set_error(const char *str)
{
snprintf(error_buffer, sizeof (error_buffer), "%s", str);
} // set_error
static inline void out_of_memory(void)
{
set_error("out of memory");
} // out_of_memory
/* D3DCompile signature */
/* This is largely taken from vkd3d_windows.h */
#ifdef _WIN32
#define D3DCOMPILER_API WINAPI
#else
# ifdef __stdcall
# undef __stdcall
# endif
# ifdef __x86_64__
# define __stdcall __attribute__((ms_abi))
# else
# if (__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 2)) || defined(__APPLE__)
# define __stdcall __attribute__((__stdcall__)) __attribute__((__force_align_arg_pointer__))
# else
# define __stdcall __attribute__((__stdcall__))
# endif
# endif
# define D3DCOMPILER_API __stdcall
#endif
typedef HRESULT(D3DCOMPILER_API *PFN_D3DCOMPILE)(
LPCVOID pSrcData,
SIZE_T SrcDataSize,
LPCSTR pSourceName,
const D3D_SHADER_MACRO *pDefines,
ID3DInclude *pInclude,
LPCSTR pEntrypoint,
LPCSTR pTarget,
UINT Flags1,
UINT Flags2,
ID3DBlob **ppCode,
ID3DBlob **ppErrorMsgs
);
/* Structs */
typedef struct d3d11ShaderMap
{
void *val;
union
{
struct
{
uint64 layoutHash;
ID3D10Blob *blob;
} vertex;
struct
{
MOJOSHADER_d3d11Shader *vshader;
} pixel;
};
} d3d11ShaderMap;
typedef struct MOJOSHADER_d3d11Shader
{
const MOJOSHADER_parseData *parseData;
uint32 refcount;
ID3D11Buffer *ubo;
size_t buflen;
unsigned char *constantData;
unsigned int mapCapacity;
unsigned int numMaps;
d3d11ShaderMap *shaderMaps;
} MOJOSHADER_d3d11Shader;
// Max entries for each register file type...
#define MAX_REG_FILE_F 8192
#define MAX_REG_FILE_I 2047
#define MAX_REG_FILE_B 2047
typedef struct MOJOSHADER_d3d11Context
{
// Allocators...
MOJOSHADER_malloc malloc_fn;
MOJOSHADER_free free_fn;
void *malloc_data;
// The constant register files...
// !!! FIXME: Man, it kills me how much memory this takes...
// !!! FIXME: ... make this dynamically allocated on demand.
float vs_reg_file_f[MAX_REG_FILE_F * 4];
int vs_reg_file_i[MAX_REG_FILE_I * 4];
uint8 vs_reg_file_b[MAX_REG_FILE_B];
float ps_reg_file_f[MAX_REG_FILE_F * 4];
int ps_reg_file_i[MAX_REG_FILE_I * 4];
uint8 ps_reg_file_b[MAX_REG_FILE_B];
// Pointer to the active ID3D11Device.
ID3D11Device *device;
// Pointer to the ID3D11DeviceContext.
ID3D11DeviceContext *deviceContext;
// Currently bound vertex and pixel shaders.
MOJOSHADER_d3d11Shader *vertexShader;
MOJOSHADER_d3d11Shader *pixelShader;
int vertexNeedsBound;
int pixelNeedsBound;
// D3DCompile function pointer.
PFN_D3DCOMPILE D3DCompileFunc;
#if !WINAPI_FAMILY_WINRT
HMODULE d3dcompilerDLL;
#endif
} MOJOSHADER_d3d11Context;
/* Uniform buffer utilities */
static inline int next_highest_alignment(int n)
{
const int align = 16;
return align * ((n + align - 1) / align);
} // next_highest_alignment
static inline void *get_uniform_buffer(MOJOSHADER_d3d11Shader *shader)
{
return (shader == NULL || shader->ubo == NULL) ? NULL : shader->ubo;
} // get_uniform_buffer
static void update_uniform_buffer(
MOJOSHADER_d3d11Context *ctx,
MOJOSHADER_d3d11Shader *shader
) {
int i, j;
float *regF; int *regI; uint8 *regB;
int needsUpdate;
size_t offset;
int idx;
int arrayCount;
void *src, *dst;
size_t size;
int *vecDst;
if (shader == NULL || shader->ubo == NULL)
return;
if (shader->parseData->shader_type == MOJOSHADER_TYPE_VERTEX)
{
regF = ctx->vs_reg_file_f;
regI = ctx->vs_reg_file_i;
regB = ctx->vs_reg_file_b;
} // if
else
{
regF = ctx->ps_reg_file_f;
regI = ctx->ps_reg_file_i;
regB = ctx->ps_reg_file_b;
} // else
// Update the buffer contents
needsUpdate = 0;
offset = 0;
for (i = 0; i < shader->parseData->uniform_count; i++)
{
if (shader->parseData->uniforms[i].constant)
continue;
idx = shader->parseData->uniforms[i].index;
arrayCount = shader->parseData->uniforms[i].array_count;
src = NULL;
dst = NULL;
size = arrayCount ? (arrayCount * 16) : 16;
switch (shader->parseData->uniforms[i].type)
{
case MOJOSHADER_UNIFORM_FLOAT:
src = &regF[4 * idx];
dst = shader->constantData + offset;
break;
case MOJOSHADER_UNIFORM_INT:
src = &regI[4 * idx];
dst = shader->constantData + offset;
break;
case MOJOSHADER_UNIFORM_BOOL:
// bool registers are a whole other mess, thanks to alignment.
// The bool field is an int4 in HLSL 4+, so we have to cast the
// bool to an int, then skip 3 ints. Super efficient, right?
vecDst = (int*) (shader->constantData + offset);
j = 0;
do
{
if (vecDst[j * 4] != regB[idx + j])
{
needsUpdate = 1;
vecDst[j * 4] = regB[idx + j];
} // if
} while (++j < arrayCount);
offset += size;
continue; // Skip the rest, do NOT break!
default:
assert(0); // This should never happen.
break;
} // switch
if (memcmp(dst, src, size) != 0)
{
memcpy(dst, src, size);
needsUpdate = 1;
} // if
offset += size;
} // for
if (needsUpdate)
{
// Map the buffer
D3D11_MAPPED_SUBRESOURCE res;
ID3D11DeviceContext_Map((ID3D11DeviceContext*) ctx->deviceContext,
(ID3D11Resource*) shader->ubo, 0,
D3D11_MAP_WRITE_DISCARD, 0, &res);
// Copy the contents
memcpy(res.pData, shader->constantData, shader->buflen);
// Unmap the buffer
ID3D11DeviceContext_Unmap(
(ID3D11DeviceContext*) ctx->deviceContext,
(ID3D11Resource*) shader->ubo,
0
);
} // if
} // update_uniform_buffer
static inline void expand_map(
MOJOSHADER_d3d11Context *ctx,
MOJOSHADER_d3d11Shader *shader
) {
if (shader->numMaps == shader->mapCapacity)
{
d3d11ShaderMap *newMap = (d3d11ShaderMap *) ctx->malloc_fn(
sizeof(d3d11ShaderMap) * shader->mapCapacity * 2,
ctx->malloc_data
);
memcpy(newMap, shader->shaderMaps,
sizeof(d3d11ShaderMap) * shader->mapCapacity);
shader->mapCapacity *= 2;
ctx->free_fn(shader->shaderMaps, ctx->malloc_data);
shader->shaderMaps = newMap;
newMap = NULL;
} // if
} // expand_map
static inline int element_is_uint(DXGI_FORMAT format)
{
return format == DXGI_FORMAT_R32G32B32A32_UINT ||
format == DXGI_FORMAT_R32G32B32_UINT ||
format == DXGI_FORMAT_R16G16B16A16_UINT ||
format == DXGI_FORMAT_R32G32_UINT ||
format == DXGI_FORMAT_R10G10B10A2_UINT ||
format == DXGI_FORMAT_R8G8B8A8_UINT ||
format == DXGI_FORMAT_R16G16_UINT ||
format == DXGI_FORMAT_R32_UINT ||
format == DXGI_FORMAT_R8G8_UINT ||
format == DXGI_FORMAT_R16_UINT ||
format == DXGI_FORMAT_R8_UINT;
} // element_is_uint
static inline int element_is_int(DXGI_FORMAT format)
{
return format == DXGI_FORMAT_R32G32B32A32_SINT ||
format == DXGI_FORMAT_R32G32B32_SINT ||
format == DXGI_FORMAT_R16G16B16A16_SINT ||
format == DXGI_FORMAT_R32G32_SINT ||
format == DXGI_FORMAT_R8G8B8A8_SINT ||
format == DXGI_FORMAT_R16G16_SINT ||
format == DXGI_FORMAT_R32_SINT ||
format == DXGI_FORMAT_R8G8_SINT ||
format == DXGI_FORMAT_R16_SINT ||
format == DXGI_FORMAT_R8_SINT;
} // element_is_int
/* Shader Compilation Utilities */
static ID3D11VertexShader *compileVertexShader(
MOJOSHADER_d3d11Context *ctx,
MOJOSHADER_d3d11Shader *shader,
const char *src,
int src_len,
ID3D10Blob **blob
) {
const MOJOSHADER_parseData *pd = shader->parseData;
HRESULT result = ctx->D3DCompileFunc(src, src_len, pd->mainfn,
NULL, NULL, pd->mainfn, "vs_4_0",
D3D_SKIP_OPT, 0, blob, blob);
if (result < 0)
{
set_error((const char *) ID3D10Blob_GetBufferPointer(*blob));
ID3D10Blob_Release(*blob);
return NULL;
} // if
void *bytecode = ID3D10Blob_GetBufferPointer(*blob);
int bytecodeLength = ID3D10Blob_GetBufferSize(*blob);
ID3D11VertexShader *ret = NULL;
ID3D11Device_CreateVertexShader(ctx->device, bytecode, bytecodeLength,
NULL, &ret);
return ret;
} // compileVertexShader
static void replaceVarname(
MOJOSHADER_d3d11Context *ctx,
const char *find,
const char *replace,
const char **source
) {
const char *srcbuf = *source;
size_t find_len = strlen(find);
size_t replace_len = strlen(replace);
#define IS_PARTIAL_TOKEN(token) \
(isalnum(*(token + find_len)) || isalnum(*(token-1)))
// How many times does `find` occur in the source buffer?
int count = 0;
char *ptr = (char *) strstr(srcbuf, find);
while (ptr != NULL)
{
if (!IS_PARTIAL_TOKEN(ptr))
count++;
ptr = strstr(ptr + find_len, find);
} // while
// How big should we make the new text buffer?
size_t oldlen = strlen(srcbuf) + 1;
size_t newlen = oldlen + (count * (replace_len - find_len));
// Easy case; just find/replace in the original buffer
if (newlen == oldlen)
{
ptr = (char *) strstr(srcbuf, find);
while (ptr != NULL)
{
if (!IS_PARTIAL_TOKEN(ptr))
memcpy(ptr, replace, replace_len);
ptr = strstr(ptr + find_len, find);
} // while
return;
} // if
// Allocate a new buffer
char *newbuf = (char *) ctx->malloc_fn(newlen, ctx->malloc_data);
memset(newbuf, '\0', newlen);
// Find + replace
char *prev_ptr = (char *) srcbuf;
char *curr_ptr = (char *) newbuf;
ptr = (char*) strstr(srcbuf, find);
while (ptr != NULL)
{
memcpy(curr_ptr, prev_ptr, ptr - prev_ptr);
curr_ptr += ptr - prev_ptr;
if (!IS_PARTIAL_TOKEN(ptr))
{
memcpy(curr_ptr, replace, replace_len);
curr_ptr += replace_len;
} // if
else
{
// Don't accidentally eat partial tokens...
memcpy(curr_ptr, find, find_len);
curr_ptr += find_len;
} // else
prev_ptr = ptr + find_len;
ptr = strstr(prev_ptr, find);
} // while
#undef IS_PARTIAL_TOKEN
// Copy the remaining part of the source buffer
memcpy(curr_ptr, prev_ptr, (srcbuf + oldlen) - prev_ptr);
// Free the source buffer
ctx->free_fn((void *) srcbuf, ctx->malloc_data);
// Point the source parameter to the new buffer
*source = newbuf;
} // replaceVarname
static char *rewritePixelShader(
MOJOSHADER_d3d11Context *ctx,
MOJOSHADER_d3d11Shader *vshader,
MOJOSHADER_d3d11Shader *pshader
) {
const MOJOSHADER_parseData *vpd = vshader->parseData;
const MOJOSHADER_parseData *ppd = pshader->parseData;
const char *_Output = "_Output" ENDLINE_STR "{" ENDLINE_STR;
const char *_Input = "_Input" ENDLINE_STR "{" ENDLINE_STR;
const char *vsrc = vpd->output;
const char *psrc = ppd->output;
const char *a, *b, *vout, *pstart, *vface, *pend;
size_t substr_len;
char *pfinal;
#define MAKE_STRBUF(buf) \
substr_len = b - a; \
buf = (const char *) ctx->malloc_fn(substr_len + 1, ctx->malloc_data); \
memset((void *) buf, '\0', substr_len + 1); \
memcpy((void *) buf, a, substr_len);
// Copy the vertex function's output struct into a buffer
a = strstr(vsrc, _Output) + strlen(_Output);
b = a;
while (*(b++) != '}');
b--;
MAKE_STRBUF(vout)
// Split up the pixel shader text...
// ...everything up to the input contents...
a = psrc;
b = strstr(psrc, _Input) + strlen(_Input);
MAKE_STRBUF(pstart)
// ...everything after the input contents.
a = b;
while (*(a++) != '}');
a--;
while (*(b++) != '\0');
MAKE_STRBUF(pend)
// Find matching semantics
int i, j;
int vfaceidx = -1;
const char *pvarname, *vvarname;
for (i = 0; i < ppd->attribute_count; i++)
{
int found_matching_vs_output_for_ps_input = 0;
for (j = 0; j < vpd->output_count; j++)
{
if (ppd->attributes[i].usage == vpd->outputs[j].usage &&
ppd->attributes[i].index == vpd->outputs[j].index)
{
found_matching_vs_output_for_ps_input = 1;
pvarname = ppd->attributes[i].name;
vvarname = vpd->outputs[j].name;
if (strcmp(pvarname, vvarname) != 0)
replaceVarname(ctx, pvarname, vvarname, &pend);
} // if
else if (strcmp(ppd->attributes[i].name, "vPos") == 0 &&
vpd->outputs[j].usage == MOJOSHADER_USAGE_POSITION &&
vpd->outputs[j].index == 0)
{
found_matching_vs_output_for_ps_input = 1;
pvarname = ppd->attributes[i].name;
vvarname = vpd->outputs[j].name;
if (strcmp(pvarname, vvarname) != 0)
replaceVarname(ctx, pvarname, vvarname, &pend);
} // else if
} // for
if (strcmp(ppd->attributes[i].name, "vFace") == 0)
vfaceidx = i;
// A vertex shader that doesn't properly initialize all its outputs
// can produce a situation where vpd->outputs is missing a matching
// entry for the PS's inputs, even though fxc will happily compile
// both shaders together as a technique in FX mode
// I don't know how to fix this yet, but a workaround is to
// correct your shader to zero-initialize all its outputs -kg
assert(found_matching_vs_output_for_ps_input);
} // for
// Special handling for VFACE
vface = (vfaceidx != -1) ? "\tbool m_vFace : SV_IsFrontFace;\n" : "";
// Concatenate the shader pieces together
substr_len = strlen(pstart) + strlen(vout) + strlen(vface) + strlen(pend);
pfinal = (char *) ctx->malloc_fn(substr_len + 1, ctx->malloc_data);
memset((void *) pfinal, '\0', substr_len + 1);
memcpy(pfinal, pstart, strlen(pstart));
memcpy(pfinal + strlen(pstart), vout, strlen(vout));
memcpy(pfinal + strlen(pstart) + strlen(vout), vface, strlen(vface));
memcpy(pfinal + strlen(pstart) + strlen(vout) + strlen(vface), pend, strlen(pend));
// Free the temporary buffers
ctx->free_fn((void *) vout, ctx->malloc_data);
ctx->free_fn((void *) pstart, ctx->malloc_data);
ctx->free_fn((void *) pend, ctx->malloc_data);
#undef MAKE_STRBUF
return pfinal;
} // spliceVertexShaderInput
static ID3D11PixelShader *compilePixelShader(
MOJOSHADER_d3d11Context *ctx,
MOJOSHADER_d3d11Shader *vshader,
MOJOSHADER_d3d11Shader *pshader
) {
ID3D11PixelShader *retval = NULL;
const char *source;
ID3DBlob *blob;
HRESULT result;
int needs_free;
if (pshader->parseData->attribute_count > 0)
{
source = rewritePixelShader(ctx, vshader, pshader);
needs_free = 1;
} // if
else
{
source = pshader->parseData->output;
needs_free = 0;
} // else
result = ctx->D3DCompileFunc(source, strlen(source),
pshader->parseData->mainfn, NULL, NULL,
pshader->parseData->mainfn, "ps_4_0",
D3D_SKIP_OPT, 0, &blob, &blob);
if (needs_free)
ctx->free_fn((void *) source, ctx->malloc_data);
if (result < 0)
{
set_error((const char *) ID3D10Blob_GetBufferPointer(blob));
return NULL;
} // if
ID3D11Device_CreatePixelShader(ctx->device,
ID3D10Blob_GetBufferPointer(blob),
ID3D10Blob_GetBufferSize(blob),
NULL, &retval);
ID3D10Blob_Release(blob);
return retval;
} // compilePixelShader
/* Public API */
MOJOSHADER_d3d11Context* MOJOSHADER_d3d11CreateContext(
void *device,
void *deviceContext,
MOJOSHADER_malloc m,
MOJOSHADER_free f,
void *malloc_d
) {
MOJOSHADER_d3d11Context *ctx;
PFN_D3DCOMPILE compileFunc;
#if WINAPI_FAMILY_WINRT
compileFunc = D3DCompile;
#else
HMODULE compileDLL;
compileDLL = LOAD_D3DCOMPILER;
if (compileDLL == NULL)
return NULL;
compileFunc = (PFN_D3DCOMPILE) LOAD_D3DCOMPILE(compileDLL);
if (compileFunc == NULL)
{
UNLOAD_D3DCOMPILER(compileDLL);
return NULL;
} // if
#endif
if (m == NULL) m = MOJOSHADER_internal_malloc;
if (f == NULL) f = MOJOSHADER_internal_free;
ctx = (MOJOSHADER_d3d11Context *) m(sizeof(MOJOSHADER_d3d11Context), malloc_d);
if (ctx == NULL)
{
out_of_memory();
goto init_fail;
} // if
memset(ctx, '\0', sizeof (MOJOSHADER_d3d11Context));
ctx->malloc_fn = m;
ctx->free_fn = f;
ctx->malloc_data = malloc_d;
// Store references to the D3D device and immediate context
ctx->device = (ID3D11Device*) device;
ctx->deviceContext = (ID3D11DeviceContext*) deviceContext;
// Store the d3dcompiler info
ctx->D3DCompileFunc = compileFunc;
#if !WINAPI_FAMILY_WINRT
ctx->d3dcompilerDLL = compileDLL;
#endif
return ctx;
init_fail:
if (ctx != NULL)
f(ctx, malloc_d);
return NULL;
} // MOJOSHADER_d3d11CreateContext
void MOJOSHADER_d3d11DestroyContext(MOJOSHADER_d3d11Context *ctx)
{
#if !WINAPI_FAMILY_WINRT
UNLOAD_D3DCOMPILER(ctx->d3dcompilerDLL);
#endif
ctx->free_fn(ctx, ctx->malloc_data);
} // MOJOSHADER_d3d11DestroyContext
MOJOSHADER_d3d11Shader *MOJOSHADER_d3d11CompileShader(MOJOSHADER_d3d11Context *ctx,
const char *mainfn,
const unsigned char *tokenbuf,
const unsigned int bufsize,
const MOJOSHADER_swizzle *swiz,
const unsigned int swizcount,
const MOJOSHADER_samplerMap *smap,
const unsigned int smapcount)
{
MOJOSHADER_malloc m = ctx->malloc_fn;
MOJOSHADER_free f = ctx->free_fn;
void *d = ctx->malloc_data;
int i;
const MOJOSHADER_parseData *pd = MOJOSHADER_parse("hlsl", mainfn, tokenbuf,
bufsize, swiz, swizcount,
smap, smapcount, m, f, d);
if (pd->error_count > 0)
{
// !!! FIXME: put multiple errors in the buffer? Don't use
// !!! FIXME: MOJOSHADER_d3d11GetError() for this?
set_error(pd->errors[0].error);
goto compile_shader_fail;
} // if
MOJOSHADER_d3d11Shader *retval = (MOJOSHADER_d3d11Shader *) m(sizeof(MOJOSHADER_d3d11Shader), d);
if (retval == NULL)
goto compile_shader_fail;
retval->parseData = pd;
retval->refcount = 1;
retval->ubo = NULL;
retval->constantData = NULL;
retval->buflen = 0;
retval->numMaps = 0;
// Allocate shader maps
retval->mapCapacity = 4; // arbitrary!
retval->shaderMaps = (d3d11ShaderMap *) m(retval->mapCapacity * sizeof(d3d11ShaderMap), d);
if (retval->shaderMaps == NULL)
goto compile_shader_fail;
memset(retval->shaderMaps, '\0', retval->mapCapacity * sizeof(d3d11ShaderMap));
// Create the uniform buffer, if needed
if (pd->uniform_count > 0)
{
// Calculate how big we need to make the buffer
for (i = 0; i < pd->uniform_count; i++)
{
const int arrayCount = pd->uniforms[i].array_count;
retval->buflen += (arrayCount ? arrayCount : 1) * 16;
} // for
D3D11_BUFFER_DESC bdesc;
bdesc.ByteWidth = next_highest_alignment(retval->buflen);
bdesc.Usage = D3D11_USAGE_DYNAMIC;
bdesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
bdesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
bdesc.MiscFlags = 0;
bdesc.StructureByteStride = 0;
ID3D11Device_CreateBuffer((ID3D11Device*) ctx->device, &bdesc, NULL,
(ID3D11Buffer**) &retval->ubo);
// Additionally allocate a CPU-side staging buffer
retval->constantData = (unsigned char *) m(retval->buflen, d);
memset(retval->constantData, '\0', retval->buflen);
} // if
return retval;
compile_shader_fail:
MOJOSHADER_freeParseData(pd);
return NULL;
} // MOJOSHADER_d3d11CompileShader
void MOJOSHADER_d3d11ShaderAddRef(MOJOSHADER_d3d11Shader *shader)
{
if (shader != NULL)
shader->refcount++;
} // MOJOSHADER_d3d11ShaderAddRef
void MOJOSHADER_d3d11DeleteShader(
MOJOSHADER_d3d11Context *ctx,
MOJOSHADER_d3d11Shader *shader
) {
if (shader != NULL)
{
if (shader->refcount > 1)
shader->refcount--;
else
{
if (shader->ubo != NULL)
{
ID3D11Buffer_Release((ID3D11Buffer*) shader->ubo);
ctx->free_fn(shader->constantData, ctx->malloc_data);
} // if
if (shader->parseData->shader_type == MOJOSHADER_TYPE_VERTEX)
{
for (int i = 0; i < shader->numMaps; i++)
{
ID3D11VertexShader_Release((ID3D11VertexShader *) shader->shaderMaps[i].val);
ID3D10Blob_Release(shader->shaderMaps[i].vertex.blob);
} // for
} // if
else if (shader->parseData->shader_type == MOJOSHADER_TYPE_PIXEL)
{
for (int i = 0; i < shader->numMaps; i++)
ID3D11PixelShader_Release((ID3D11PixelShader *) shader->shaderMaps[i].val);
} // else if
ctx->free_fn(shader->shaderMaps, ctx->malloc_data);
shader->shaderMaps = NULL;
MOJOSHADER_freeParseData(shader->parseData);
ctx->free_fn(shader, ctx->malloc_data);
} // else
} // if
} // MOJOSHADER_d3d11DeleteShader
const MOJOSHADER_parseData *MOJOSHADER_d3d11GetShaderParseData(
MOJOSHADER_d3d11Shader *shader)
{
return (shader != NULL) ? shader->parseData : NULL;
} // MOJOSHADER_d3d11GetParseData
void MOJOSHADER_d3d11BindShaders(
MOJOSHADER_d3d11Context *ctx,
MOJOSHADER_d3d11Shader *vshader,
MOJOSHADER_d3d11Shader *pshader
) {
// Use the last bound shaders in case of NULL
if (vshader != NULL)
{
ctx->vertexShader = vshader;
ctx->vertexNeedsBound = 1;
} // if
if (pshader != NULL)
{
ctx->pixelShader = pshader;
ctx->pixelNeedsBound = 1;
} // if
} // MOJOSHADER_d3d11BindShaders
void MOJOSHADER_d3d11GetBoundShaders(
MOJOSHADER_d3d11Context *ctx,
MOJOSHADER_d3d11Shader **vshader,
MOJOSHADER_d3d11Shader **pshader
) {
*vshader = ctx->vertexShader;
*pshader = ctx->pixelShader;
} // MOJOSHADER_d3d11GetBoundShaders
void MOJOSHADER_d3d11MapUniformBufferMemory(
MOJOSHADER_d3d11Context *ctx,
float **vsf, int **vsi, unsigned char **vsb,
float **psf, int **psi, unsigned char **psb
) {
*vsf = ctx->vs_reg_file_f;
*vsi = ctx->vs_reg_file_i;
*vsb = ctx->vs_reg_file_b;
*psf = ctx->ps_reg_file_f;
*psi = ctx->ps_reg_file_i;
*psb = ctx->ps_reg_file_b;
} // MOJOSHADER_d3d11MapUniformBufferMemory
void MOJOSHADER_d3d11UnmapUniformBufferMemory(MOJOSHADER_d3d11Context *ctx)
{
/* This has nothing to do with unmapping memory
* and everything to do with updating uniform
* buffers with the latest parameter contents.
*/
MOJOSHADER_d3d11Shader *vs, *ps;
MOJOSHADER_d3d11GetBoundShaders(ctx, &vs, &ps);
update_uniform_buffer(ctx, vs);
update_uniform_buffer(ctx, ps);
} // MOJOSHADER_d3d11UnmapUniformBufferMemory
int MOJOSHADER_d3d11GetVertexAttribLocation(MOJOSHADER_d3d11Shader *vert,
MOJOSHADER_usage usage, int index)
{
if (vert == NULL)
return -1;
for (int i = 0; i < vert->parseData->attribute_count; i++)
{
if (vert->parseData->attributes[i].usage == usage &&
vert->parseData->attributes[i].index == index)
{
return i;
} // if
} // for
// failure, couldn't find requested attribute
return -1;
} // MOJOSHADER_d3d11GetVertexAttribLocation
void MOJOSHADER_d3d11CompileVertexShader(
MOJOSHADER_d3d11Context *ctx,
unsigned long long inputLayoutHash,
void* elements,
int elementCount,
void **bytecode,
int *bytecodeLength
) {
MOJOSHADER_d3d11Shader *vshader = ctx->vertexShader;
ID3D10Blob *blob;
// Don't compile if there's already a mapping for this layout.
for (int i = 0; i < vshader->numMaps; i++)
{
if (inputLayoutHash == vshader->shaderMaps[i].vertex.layoutHash)
{
blob = vshader->shaderMaps[i].vertex.blob;
*bytecode = ID3D10Blob_GetBufferPointer(blob);
*bytecodeLength = ID3D10Blob_GetBufferSize(blob);
return;
} // if
} // for
// Check for and replace non-float types
D3D11_INPUT_ELEMENT_DESC *d3dElements = (D3D11_INPUT_ELEMENT_DESC*) elements;
const char *origSource = vshader->parseData->output;
int srcLength = vshader->parseData->output_len;
char *newSource = (char*) origSource;
for (int i = 0; i < elementCount; i += 1)
{
D3D11_INPUT_ELEMENT_DESC e = d3dElements[i];
const char *replace;
if (element_is_uint(e.Format))
replace = " uint4";
else if (element_is_int(e.Format))
replace = " int4";
else
replace = NULL;
if (replace != NULL)
{
char sem[16];
memset(sem, '\0', sizeof(sem));
snprintf(sem, sizeof(sem), "%s%d", e.SemanticName, e.SemanticIndex);
// !!! FIXME: POSITIONT has no index. What to do? -caleb
if (newSource == origSource)
{
newSource = (char *) ctx->malloc_fn(srcLength + 1,
ctx->malloc_data);
strcpy(newSource, origSource);
} // if
char *ptr = strstr(newSource, sem);
assert(ptr != NULL && "Could not find semantic in shader source!");
int spaces = 0;
while (spaces < 3)
if (*(--ptr) == ' ') spaces++;
memcpy(ptr - strlen("float4"), replace, strlen(replace));
} // if
} // for
// Expand the map array, if needed
expand_map(ctx, vshader);
// Add the new mapping
vshader->shaderMaps[vshader->numMaps].vertex.layoutHash = inputLayoutHash;
ID3D11VertexShader *vs = compileVertexShader(ctx, vshader, newSource,
srcLength, &blob);
if (newSource != origSource)
ctx->free_fn((void *) newSource, ctx->malloc_data);
vshader->shaderMaps[ctx->vertexShader->numMaps].val = vs;
vshader->shaderMaps[ctx->vertexShader->numMaps].vertex.blob = blob;
ctx->vertexShader->numMaps++;
assert(vs != NULL);
// Return the bytecode info
*bytecode = ID3D10Blob_GetBufferPointer(blob);
*bytecodeLength = ID3D10Blob_GetBufferSize(blob);
} // MOJOSHADER_d3d11CompileVertexShader
void MOJOSHADER_d3d11ProgramReady(
MOJOSHADER_d3d11Context *ctx,
unsigned long long inputLayoutHash
) {
MOJOSHADER_d3d11Shader *vshader = ctx->vertexShader;
MOJOSHADER_d3d11Shader *pshader = ctx->pixelShader;
// Vertex shader...
if (ctx->vertexNeedsBound)
{
ID3D11VertexShader *realVS = NULL;
for (int i = 0; i < vshader->numMaps; i++)
{
if (inputLayoutHash == vshader->shaderMaps[i].vertex.layoutHash)
{
realVS = (ID3D11VertexShader *) vshader->shaderMaps[i].val;
break;
} // if
} // for
assert(realVS != NULL);
ID3D11DeviceContext_VSSetShader(ctx->deviceContext, realVS, NULL, 0);
ID3D11DeviceContext_VSSetConstantBuffers(ctx->deviceContext, 0, 1,
&vshader->ubo);
ctx->vertexNeedsBound = 0;
} // if
// Pixel shader...
if (ctx->pixelNeedsBound)
{
// Is there already a mapping for the current vertex shader?
ID3D11PixelShader *realPS = NULL;
for (int i = 0; i < pshader->numMaps; i++)
{
if (pshader->shaderMaps[i].pixel.vshader == vshader)
{
realPS = (ID3D11PixelShader *) pshader->shaderMaps[i].val;
break;
} // if
} // for
// We have to create a new vertex/pixel shader mapping...
if (realPS == NULL)
{
// Expand the map array, if needed
expand_map(ctx, pshader);
// Add the new mapping
pshader->shaderMaps[pshader->numMaps].pixel.vshader = vshader;
realPS = compilePixelShader(ctx, vshader, pshader);
pshader->shaderMaps[pshader->numMaps].val = realPS;
pshader->numMaps++;
assert(realPS != NULL);
} // if
ID3D11DeviceContext_PSSetShader(ctx->deviceContext, realPS, NULL, 0);
ID3D11DeviceContext_PSSetConstantBuffers(ctx->deviceContext, 0, 1,
&pshader->ubo);
ctx->pixelNeedsBound = 0;
} // if
} // MOJOSHADER_d3d11ProgramReady
const char *MOJOSHADER_d3d11GetError(MOJOSHADER_d3d11Context *ctx)
{
return error_buffer;
} // MOJOSHADER_d3d11GetError
#endif /* SUPPORT_PROFILE_HLSL */
// end of mojoshader_d3d11.c ...