cnq3/code/renderer/tr_backend_d3d11.cpp

2422 lines
80 KiB
C++
Raw Normal View History

/*
===========================================================================
Copyright (C) 2019 Gian 'myT' Schellenbaum
This file is part of Challenge Quake 3 (CNQ3).
Challenge Quake 3 is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
Challenge Quake 3 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with Challenge Quake 3. If not, see <https://www.gnu.org/licenses/>.
===========================================================================
*/
// Direct3D 11 rendering back-end
#if defined(_WIN32)
#include "tr_local.h"
#include <Windows.h>
#include <d3d11.h>
#include <dxgi.h>
#include "hlsl/generic_vs.h"
#include "hlsl/generic_ps.h"
#include "hlsl/generic_a_ps.h"
#include "hlsl/generic_d_ps.h"
#include "hlsl/generic_ad_ps.h"
#include "hlsl/post_vs.h"
#include "hlsl/post_ps.h"
#include "hlsl/dl_vs.h"
#include "hlsl/dl_ps.h"
#include "hlsl/sprite_vs.h"
#include "hlsl/sprite_ps.h"
#include "hlsl/mip_start_cs.h"
#include "hlsl/mip_pass_cs.h"
#include "hlsl/mip_end_cs.h"
struct ShaderDesc
{
const void* code;
size_t size;
const char* name;
};
static ShaderDesc genericPixelShaders[4] =
{
{ g_generic_ps, ARRAY_LEN(g_generic_ps), "generic pixel shader" },
{ g_generic_a_ps, ARRAY_LEN(g_generic_a_ps), "generic A2C pixel shader" },
{ g_generic_d_ps, ARRAY_LEN(g_generic_d_ps), "generic dithered pixel shader" },
{ g_generic_ad_ps, ARRAY_LEN(g_generic_ad_ps), "generic dithered A2C pixel shader" }
};
#if defined(near)
# undef near
#endif
#if defined(far)
# undef far
#endif
#define MAX_GPU_TEXTURE_SIZE 2048 // instead of D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION
#define BLEND_STATE_COUNT (D3D11_BLEND_SRC_ALPHA_SAT + 1)
/*
Current info:
- feature level 10.1 minimum
- feature level 11.0 for mip-map generation with compute
- shader target 4.1 for graphics (SV_VertexID, unsized Texture2DMS)
- shader target 5.0 for compute (typed UAVs)
To look at:
- near clip plane seems to be further in the GL2 back-end in 3D land
- find/use D3DDDIERR_DEVICEREMOVED? are the docs correct about it?
*/
enum AlphaTest
{
AT_ALWAYS,
AT_GREATER_THAN_0,
AT_LESS_THAN_HALF,
AT_GREATER_OR_EQUAL_TO_HALF
};
enum PipelineId
{
PID_GENERIC,
PID_SOFT_SPRITE,
PID_DYNAMIC_LIGHT,
PID_POST_PROCESS,
PID_COUNT
};
enum ErrorMode
{
EM_FATAL,
EM_SILENT
};
enum VertexBufferId
{
VB_POSITION,
VB_NORMAL,
VB_TEXCOORD,
VB_TEXCOORD2,
VB_COLOR,
VB_COUNT
};
// @NOTE: MSDN says "you must set the ByteWidth value of D3D11_BUFFER_DESC in multiples of 16"
#pragma pack(push, 16)
struct GenericVSData
{
float modelViewMatrix[16];
float projectionMatrix[16];
float clipPlane[4];
};
struct GenericPSData
{
uint32_t alphaTest; // AlphaTest enum
uint32_t texEnv; // texEnv_t enum
float seed[2];
float invGamma;
float invBrightness;
float noiseScale;
float dummy;
};
struct SoftSpriteVSData
{
float modelViewMatrix[16];
float projectionMatrix[16];
float clipPlane[4];
};
struct SoftSpritePSData
{
uint32_t alphaTest; // AlphaTest enum
float proj22;
float proj32;
float additive;
float distance;
float offset;
uint32_t dummy[2];
};
struct DynamicLightVSData
{
float modelViewMatrix[16];
float projectionMatrix[16];
float clipPlane[4];
float osLightPos[4];
float osEyePos[4];
};
struct DynamicLightPSData
{
float lightColor[3];
float lightRadius;
uint32_t alphaTest; // AlphaTest enum
uint32_t dummy[3];
};
struct PostVSData
{
float scaleX;
float scaleY;
float dummy[2];
};
struct PostPSData
{
float gamma;
float brightness;
float greyscale;
float dummy;
};
struct Down4CSData
{
float weights[4];
uint32_t maxSize[2];
uint32_t scale[2];
uint32_t offset[2];
uint32_t clampMode; // 0 = repeat
uint32_t dummy;
};
struct LinearToGammaCSData
{
float blendColor[4];
float intensity;
float invGamma;
float dummy[2];
};
struct GammaToLinearCSData
{
float gamma;
float dummy[3];
};
#pragma pack(pop)
struct Texture
{
ID3D11Texture2D* texture;
ID3D11ShaderResourceView* view;
};
struct Pipeline
{
ID3D11VertexShader* vertexShader;
ID3D11PixelShader* pixelShader;
ID3D11InputLayout* inputLayout; // can be NULL
ID3D11Buffer* vertexBuffer; // can be NULL
ID3D11Buffer* pixelBuffer; // can be NULL
};
struct MipGenTexture
{
ID3D11Texture2D* texture;
ID3D11ShaderResourceView* srv;
ID3D11UnorderedAccessView* uav;
};
struct VertexBuffer
{
ID3D11Buffer* buffer;
int itemSize;
int capacity;
int writeIndex;
int readIndex;
qbool discard;
};
struct AdapterInfo
{
qbool valid;
int dedicatedSystemMemoryMB;
int dedicatedVideoMemoryMB;
int sharedSystemMemoryMB;
};
struct FrameQueries
{
ID3D11Query* disjoint;
ID3D11Query* frameStart;
ID3D11Query* frameEnd;
qbool valid;
};
struct Direct3D
{
// constant buffer data
PostVSData postVSData;
PostPSData postPSData;
float modelViewMatrix[16];
float projectionMatrix[16];
float clipPlane[4];
float osLightPos[4];
float osEyePos[4];
float lightColor[3];
float lightRadius;
AlphaTest alphaTest;
texEnv_t texEnv;
float frameSeed[2];
Texture textures[MAX_DRAWIMAGES];
int textureCount;
ID3D11SamplerState* samplerStates[TW_COUNT * 2];
int samplerStateIndices[2];
ID3D11BlendState* blendStates[2 * BLEND_STATE_COUNT * BLEND_STATE_COUNT];
int blendStateIndex;
ID3D11DepthStencilState* depthStencilStates[8];
int depthStencilStateIndex;
ID3D11RasterizerState* rasterStates[12];
int rasterStateIndex;
Pipeline pipelines[PID_COUNT];
PipelineId pipelineIndex;
MipGenTexture mipGenTextures[3]; // 0,1=float16 2=uint8
VertexBuffer vertexBuffers[VB_COUNT];
VertexBuffer indexBuffer;
// for the calls to IASetVertexBuffers
VertexBufferId vbIds[VB_COUNT];
ID3D11Buffer* vbBuffers[VB_COUNT];
UINT vbStrides[VB_COUNT];
int vbCount;
qbool splitBufferOffsets;
ID3D11Device* device;
ID3D11DeviceContext* context;
IDXGISwapChain* swapChain;
ID3D11Texture2D* backBufferTexture;
ID3D11RenderTargetView* backBufferRTView;
ID3D11Texture2D* renderTargetTextureMS;
ID3D11RenderTargetView* renderTargetViewMS;
ID3D11Texture2D* resolveTexture;
ID3D11ShaderResourceView* resolveTextureShaderView;
ID3D11Texture2D* depthStencilTexture;
ID3D11DepthStencilView* depthStencilView;
ID3D11ShaderResourceView* depthStencilShaderView;
ID3D11Texture2D* readbackTexture; // allowed to be NULL!
HMODULE library;
ID3D11ComputeShader* mipGammaToLinearComputeShader;
ID3D11ComputeShader* mipLinearToGammaComputeShader;
ID3D11ComputeShader* mipDownSampleComputeShader;
ID3D11Buffer* mipDownSampleConstBuffer;
ID3D11Buffer* mipLinearToGammaConstBuffer;
ID3D11Buffer* mipGammaToLinearConstBuffer;
FrameQueries frameQueries[32];
int frameQueriesWriteIndex;
int frameQueriesReadIndex;
// cached when starting sky rendering
float oldSkyClipPlane[4];
D3D11_VIEWPORT oldSkyViewport;
ErrorMode errorMode;
AdapterInfo adapterInfo;
};
__declspec(align(16)) static Direct3D d3d;
#define COM_RELEASE(p) do { if(p) { p->Release(); p = NULL; } } while((void)0,0)
#define COM_RELEASE_ARRAY(a) do { for(int i = 0; i < ARRAY_LEN(a); ++i) { COM_RELEASE(a[i]); } } while((void)0,0)
static void GAL_UpdateTexture(image_t* image, int mip, int x, int y, int w, int h, const void* data);
static const char* GetSystemErrorString(HRESULT hr)
{
// FormatMessage might not always give us the string we want but that's ok,
// we always print the original error code anyhow
static char systemErrorStr[1024];
const DWORD written = FormatMessageA(
FORMAT_MESSAGE_FROM_SYSTEM, NULL, (DWORD)hr, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
systemErrorStr, sizeof(systemErrorStr) - 1, NULL);
if(written == 0)
{
Q_strncpyz(systemErrorStr, "???", sizeof(systemErrorStr));
}
return systemErrorStr;
}
static qbool Check(HRESULT hr, const char* function)
{
if(SUCCEEDED(hr))
{
return qtrue;
}
if(d3d.errorMode == EM_FATAL)
{
ri.Error(ERR_FATAL, va("'%s' failed with code 0x%08X (%s)", function, (unsigned int)hr, GetSystemErrorString(hr)));
}
return qfalse;
}
static qbool CheckAndName(HRESULT hr, const char* function, ID3D11DeviceChild* resource, const char* resourceName)
{
if(SUCCEEDED(hr))
{
resource->SetPrivateData(WKPDID_D3DDebugObjectName, strlen(resourceName), resourceName);
return qtrue;
}
if(d3d.errorMode == EM_FATAL)
{
ri.Error(ERR_FATAL, va("'%s' failed to create '%s' with code 0x%08X (%s)", function, resourceName, (unsigned int)hr, GetSystemErrorString(hr)));
}
return qfalse;
}
static qbool D3D11_CreateRenderTargetView(ID3D11Resource* pResource, const D3D11_RENDER_TARGET_VIEW_DESC* pDesc, ID3D11RenderTargetView** ppRTView, const char* name)
{
const HRESULT hr = d3d.device->CreateRenderTargetView(pResource, pDesc, ppRTView);
return CheckAndName(hr, "CreateRenderTargetView", *ppRTView, name);
}
static qbool D3D11_CreateTexture2D(const D3D11_TEXTURE2D_DESC* pDesc, const D3D11_SUBRESOURCE_DATA* pInitialData, ID3D11Texture2D** ppTexture2D, const char* name)
{
const HRESULT hr = d3d.device->CreateTexture2D(pDesc, pInitialData, ppTexture2D);
return CheckAndName(hr, "CreateTexture2D", *ppTexture2D, name);
}
static qbool D3D11_CreateShaderResourceView(ID3D11Resource* pResource, const D3D11_SHADER_RESOURCE_VIEW_DESC* pDesc, ID3D11ShaderResourceView** ppSRView, const char* name)
{
const HRESULT hr = d3d.device->CreateShaderResourceView(pResource, pDesc, ppSRView);
return CheckAndName(hr, "CreateShaderResourceView", *ppSRView, name);
}
static qbool D3D11_CreateUnorderedAccessView(ID3D11Resource* pResource, const D3D11_UNORDERED_ACCESS_VIEW_DESC* pDesc, ID3D11UnorderedAccessView** ppUAView, const char* name)
{
const HRESULT hr = d3d.device->CreateUnorderedAccessView(pResource, pDesc, ppUAView);
return CheckAndName(hr, "CreateUnorderedAccessView", *ppUAView, name);
}
static qbool D3D11_CreateVertexShader(const void* pShaderBytecode, SIZE_T BytecodeLength, ID3D11ClassLinkage* pClassLinkage, ID3D11VertexShader** ppVertexShader, const char* name)
{
const HRESULT hr = d3d.device->CreateVertexShader(pShaderBytecode, BytecodeLength, pClassLinkage, ppVertexShader);
return CheckAndName(hr, "CreateVertexShader", *ppVertexShader, name);
}
static qbool D3D11_CreatePixelShader(const void* pShaderBytecode, SIZE_T BytecodeLength, ID3D11ClassLinkage* pClassLinkage, ID3D11PixelShader** ppPixelShader, const char* name)
{
const HRESULT hr = d3d.device->CreatePixelShader(pShaderBytecode, BytecodeLength, pClassLinkage, ppPixelShader);
return CheckAndName(hr, "CreatePixelShader", *ppPixelShader, name);
}
static qbool D3D11_CreateComputeShader(const void* pShaderBytecode, SIZE_T BytecodeLength, ID3D11ClassLinkage* pClassLinkage, ID3D11ComputeShader** ppComputeShader, const char* name)
{
const HRESULT hr = d3d.device->CreateComputeShader(pShaderBytecode, BytecodeLength, pClassLinkage, ppComputeShader);
return CheckAndName(hr, "CreateComputeShader", *ppComputeShader, name);
}
static qbool D3D11_CreateBuffer(const D3D11_BUFFER_DESC* pDesc, const D3D11_SUBRESOURCE_DATA* pInitialData, ID3D11Buffer** ppBuffer, const char* name)
{
const HRESULT hr = d3d.device->CreateBuffer(pDesc, pInitialData, ppBuffer);
return CheckAndName(hr, "CreateBuffer", *ppBuffer, name);
}
static qbool D3D11_CreateInputLayout(const D3D11_INPUT_ELEMENT_DESC* pInputElementDescs, UINT NumElements, const void* pShaderBytecodeWithInputSignature, SIZE_T BytecodeLength, ID3D11InputLayout** ppInputLayout, const char* name)
{
const HRESULT hr = d3d.device->CreateInputLayout(pInputElementDescs, NumElements, pShaderBytecodeWithInputSignature, BytecodeLength, ppInputLayout);
return CheckAndName(hr, "CreateInputLayout", *ppInputLayout, name);
}
static const char* GetDeviceRemovedReason()
{
switch(d3d.device->GetDeviceRemovedReason())
{
case DXGI_ERROR_DEVICE_HUNG: return "device hung";
case DXGI_ERROR_DEVICE_REMOVED: return "device removed";
case DXGI_ERROR_DEVICE_RESET: return "device reset";
case DXGI_ERROR_DRIVER_INTERNAL_ERROR: return "internal driver error";
case DXGI_ERROR_INVALID_CALL: return "invalid call";
default: return "unknown";
}
}
static AlphaTest GetAlphaTest(unsigned int stateBits)
{
switch(stateBits & GLS_ATEST_BITS)
{
case 0: return AT_ALWAYS;
case GLS_ATEST_GT_0: return AT_GREATER_THAN_0;
case GLS_ATEST_LT_80: return AT_LESS_THAN_HALF;
case GLS_ATEST_GE_80: return AT_GREATER_OR_EQUAL_TO_HALF;
default: return AT_ALWAYS;
}
}
static D3D11_TEXTURE_ADDRESS_MODE GetTextureAddressMode(textureWrap_t wrap)
{
switch(wrap)
{
case TW_CLAMP_TO_EDGE: return D3D11_TEXTURE_ADDRESS_CLAMP;
case TW_REPEAT: return D3D11_TEXTURE_ADDRESS_WRAP;
default: return D3D11_TEXTURE_ADDRESS_CLAMP;
}
}
static DXGI_FORMAT GetTextureFormat(textureFormat_t f)
{
switch(f)
{
case TF_RGBA8:
default: return DXGI_FORMAT_R8G8B8A8_UNORM;
}
}
static D3D11_CULL_MODE GetCullMode(cullType_t t)
{
switch(t)
{
case CT_BACK_SIDED: return D3D11_CULL_BACK;
case CT_FRONT_SIDED: return D3D11_CULL_FRONT;
case CT_TWO_SIDED: return D3D11_CULL_NONE;
default: return D3D11_CULL_NONE;
}
}
static D3D11_BLEND GetSourceBlend(unsigned int stateBits)
{
switch(stateBits & GLS_SRCBLEND_BITS)
{
case GLS_SRCBLEND_ZERO: return D3D11_BLEND_ZERO;
case GLS_SRCBLEND_ONE: return D3D11_BLEND_ONE;
case GLS_SRCBLEND_DST_COLOR: return D3D11_BLEND_DEST_COLOR;
case GLS_SRCBLEND_ONE_MINUS_DST_COLOR: return D3D11_BLEND_INV_DEST_COLOR;
case GLS_SRCBLEND_SRC_ALPHA: return D3D11_BLEND_SRC_ALPHA;
case GLS_SRCBLEND_ONE_MINUS_SRC_ALPHA: return D3D11_BLEND_INV_SRC_ALPHA;
case GLS_SRCBLEND_DST_ALPHA: return D3D11_BLEND_DEST_ALPHA;
case GLS_SRCBLEND_ONE_MINUS_DST_ALPHA: return D3D11_BLEND_INV_DEST_ALPHA;
case GLS_SRCBLEND_ALPHA_SATURATE: return D3D11_BLEND_SRC_ALPHA_SAT;
default: return D3D11_BLEND_ONE;
}
}
static D3D11_BLEND GetDestinationBlend(unsigned int stateBits)
{
switch(stateBits & GLS_DSTBLEND_BITS)
{
case GLS_DSTBLEND_ZERO: return D3D11_BLEND_ZERO;
case GLS_DSTBLEND_ONE: return D3D11_BLEND_ONE;
case GLS_DSTBLEND_SRC_COLOR: return D3D11_BLEND_SRC_COLOR;
case GLS_DSTBLEND_ONE_MINUS_SRC_COLOR: return D3D11_BLEND_INV_SRC_COLOR;
case GLS_DSTBLEND_SRC_ALPHA: return D3D11_BLEND_SRC_ALPHA;
case GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA: return D3D11_BLEND_INV_SRC_ALPHA;
case GLS_DSTBLEND_DST_ALPHA: return D3D11_BLEND_DEST_ALPHA;
case GLS_DSTBLEND_ONE_MINUS_DST_ALPHA: return D3D11_BLEND_INV_DEST_ALPHA;
default: return D3D11_BLEND_ONE;
}
}
static void ResetShaderData(ID3D11Resource* buffer, const void* data, size_t bytes)
{
D3D11_MAPPED_SUBRESOURCE ms;
HRESULT hr = d3d.context->Map(buffer, 0, D3D11_MAP_WRITE_DISCARD, NULL, &ms);
if(FAILED(hr))
{
ri.Error(ERR_FATAL, "Map failed");
return;
}
memcpy(ms.pData, data, bytes);
d3d.context->Unmap(buffer, NULL);
}
static void AppendVertexData(VertexBuffer* buffer, const void* data, int itemCount)
{
D3D11_MAP mapType = D3D11_MAP_WRITE_NO_OVERWRITE;
if(buffer->discard || buffer->writeIndex + itemCount > buffer->capacity)
{
buffer->discard = qfalse;
buffer->writeIndex = 0;
mapType = D3D11_MAP_WRITE_DISCARD;
}
if(data != NULL || mapType == D3D11_MAP_WRITE_DISCARD)
{
D3D11_MAPPED_SUBRESOURCE ms;
HRESULT hr = d3d.context->Map(buffer->buffer, 0, mapType, NULL, &ms);
if(FAILED(hr))
{
ri.Error(ERR_FATAL, "Map failed");
return;
}
if(data != NULL)
{
memcpy((byte*)ms.pData + buffer->writeIndex * buffer->itemSize, data, itemCount * buffer->itemSize);
}
d3d.context->Unmap(buffer->buffer, NULL);
}
buffer->readIndex = buffer->writeIndex;
buffer->writeIndex += itemCount;
}
static void AppendVertexDataGroup(const void* data[VB_COUNT], int vertexCount)
{
for(int i = 0; i < VB_COUNT; ++i)
{
AppendVertexData(&d3d.vertexBuffers[i], data[i], vertexCount);
}
}
static void UploadPendingShaderData()
{
if((unsigned)d3d.pipelineIndex >= PID_COUNT)
{
return;
}
const PipelineId pid = d3d.pipelineIndex;
Pipeline* const pipeline = &d3d.pipelines[pid];
if(pid == PID_GENERIC)
{
GenericVSData vsData;
GenericPSData psData;
memcpy(vsData.modelViewMatrix, d3d.modelViewMatrix, sizeof(vsData.modelViewMatrix));
memcpy(vsData.projectionMatrix, d3d.projectionMatrix, sizeof(vsData.projectionMatrix));
memcpy(vsData.clipPlane, d3d.clipPlane, sizeof(vsData.clipPlane));
psData.alphaTest = d3d.alphaTest;
psData.texEnv = d3d.texEnv;
psData.seed[0] = d3d.frameSeed[0];
psData.seed[1] = d3d.frameSeed[1];
psData.invGamma = 1.0f / r_gamma->value;
psData.invBrightness = 1.0f / r_brightness->value;
psData.noiseScale = backEnd.projection2D ? 0.0f : r_noiseScale->value;
ResetShaderData(pipeline->vertexBuffer, &vsData, sizeof(vsData));
ResetShaderData(pipeline->pixelBuffer, &psData, sizeof(psData));
}
else if(pid == PID_SOFT_SPRITE)
{
SoftSpriteVSData vsData;
SoftSpritePSData psData;
memcpy(vsData.modelViewMatrix, d3d.modelViewMatrix, sizeof(vsData.modelViewMatrix));
memcpy(vsData.projectionMatrix, d3d.projectionMatrix, sizeof(vsData.projectionMatrix));
memcpy(vsData.clipPlane, d3d.clipPlane, sizeof(vsData.clipPlane));
psData.alphaTest = d3d.alphaTest;
psData.proj22 = -vsData.projectionMatrix[2 * 4 + 2];
psData.proj32 = vsData.projectionMatrix[3 * 4 + 2];
psData.additive = tess.shader->softSprite == SST_ADDITIVE ? 1.0f : 0.0f;
psData.distance = tess.shader->softSpriteDistance;
psData.offset = tess.shader->softSpriteOffset;
ResetShaderData(pipeline->vertexBuffer, &vsData, sizeof(vsData));
ResetShaderData(pipeline->pixelBuffer, &psData, sizeof(psData));
}
else if(pid == PID_DYNAMIC_LIGHT)
{
DynamicLightVSData vsData;
DynamicLightPSData psData;
memcpy(vsData.modelViewMatrix, d3d.modelViewMatrix, sizeof(vsData.modelViewMatrix));
memcpy(vsData.projectionMatrix, d3d.projectionMatrix, sizeof(vsData.projectionMatrix));
memcpy(vsData.clipPlane, d3d.clipPlane, sizeof(vsData.clipPlane));
memcpy(vsData.osEyePos, d3d.osEyePos, sizeof(vsData.osEyePos));
memcpy(vsData.osLightPos, d3d.osLightPos, sizeof(vsData.osLightPos));
psData.alphaTest = d3d.alphaTest;
memcpy(psData.lightColor, d3d.lightColor, sizeof(psData.lightColor));
psData.lightRadius = d3d.lightRadius;
ResetShaderData(pipeline->vertexBuffer, &vsData, sizeof(vsData));
ResetShaderData(pipeline->pixelBuffer, &psData, sizeof(psData));
}
else if(pid == PID_POST_PROCESS)
{
ResetShaderData(pipeline->vertexBuffer, &d3d.postVSData, sizeof(d3d.postVSData));
ResetShaderData(pipeline->pixelBuffer, &d3d.postPSData, sizeof(d3d.postPSData));
}
}
static int ComputeSamplerStateIndex(int textureWrap, int bilinear)
{
return bilinear * TW_COUNT + textureWrap;
}
static void ApplySamplerState(UINT slot, textureWrap_t textureWrap, qbool bilinear)
{
const int index = ComputeSamplerStateIndex(textureWrap, bilinear);
if(index == d3d.samplerStateIndices[slot])
{
return;
}
d3d.context->PSSetSamplers(slot, 1, &d3d.samplerStates[index]);
d3d.samplerStateIndices[slot] = index;
}
static void DrawIndexed(int indexCount)
{
if(d3d.splitBufferOffsets)
{
UINT offsets[VB_COUNT];
for(int i = 0; i < d3d.vbCount; ++i)
{
VertexBuffer* const vb = &d3d.vertexBuffers[d3d.vbIds[i]];
offsets[i] = vb->readIndex * vb->itemSize; // in bytes, not vertices
}
d3d.context->IASetVertexBuffers(0, d3d.vbCount, d3d.vbBuffers, d3d.vbStrides, offsets);
d3d.context->DrawIndexed(indexCount, d3d.indexBuffer.readIndex, 0);
}
else
{
d3d.context->DrawIndexed(indexCount, d3d.indexBuffer.readIndex, d3d.vertexBuffers[VB_POSITION].readIndex);
}
}
static void ApplyPipeline(PipelineId index)
{
if(index == d3d.pipelineIndex || (unsigned)index >= PID_COUNT)
{
return;
}
Pipeline* const pipeline = &d3d.pipelines[index];
if(pipeline->inputLayout)
{
d3d.context->IASetInputLayout(pipeline->inputLayout);
int count = 0;
VertexBufferId* const ids = d3d.vbIds;
if(index == PID_GENERIC)
{
ids[count++] = VB_POSITION;
ids[count++] = VB_COLOR;
ids[count++] = VB_TEXCOORD;
ids[count++] = VB_TEXCOORD2;
}
else if(index == PID_SOFT_SPRITE)
{
ids[count++] = VB_POSITION;
ids[count++] = VB_COLOR;
ids[count++] = VB_TEXCOORD;
}
else if(index == PID_DYNAMIC_LIGHT)
{
ids[count++] = VB_POSITION;
ids[count++] = VB_NORMAL;
ids[count++] = VB_COLOR;
ids[count++] = VB_TEXCOORD;
}
d3d.vbCount = count;
for(int i = 0; i < count; ++i)
{
VertexBuffer* const vb = &d3d.vertexBuffers[ids[i]];
d3d.vbBuffers[i] = vb->buffer;
d3d.vbStrides[i] = vb->itemSize;
}
if(!d3d.splitBufferOffsets)
{
UINT offsets[VB_COUNT] = { 0 };
d3d.context->IASetVertexBuffers(0, count, d3d.vbBuffers, d3d.vbStrides, offsets);
}
}
else
{
d3d.context->IASetInputLayout(NULL);
d3d.context->IASetVertexBuffers(0, 0, NULL, NULL, NULL);
d3d.vbCount = 0;
}
d3d.context->VSSetShader(pipeline->vertexShader, NULL, 0);
d3d.context->PSSetShader(pipeline->pixelShader, NULL, 0);
if(pipeline->vertexBuffer)
{
d3d.context->VSSetConstantBuffers(0, 1, &pipeline->vertexBuffer);
}
if(pipeline->pixelBuffer)
{
d3d.context->PSSetConstantBuffers(0, 1, &pipeline->pixelBuffer);
}
if(index == PID_POST_PROCESS)
{
d3d.context->OMSetRenderTargets(1, &d3d.backBufferRTView, NULL);
}
else if(index == PID_SOFT_SPRITE)
{
d3d.context->OMSetRenderTargets(1, &d3d.renderTargetViewMS, NULL);
d3d.context->PSSetShaderResources(1, 1, &d3d.depthStencilShaderView);
ApplySamplerState(1, TW_CLAMP_TO_EDGE, qtrue); // @TODO: nearest neighbor mode?
}
else
{
d3d.context->PSSetShaderResources(1, 1, &d3d.textures[0].view); // make sure the depth shader view isn't bound anymore
d3d.context->OMSetRenderTargets(1, &d3d.renderTargetViewMS, d3d.depthStencilView);
}
d3d.pipelineIndex = index;
}
static void ApplyViewport(int x, int y, int w, int h, int th)
{
const int top = th - y - h;
D3D11_VIEWPORT vp;
vp.TopLeftX = x;
vp.TopLeftY = top;
vp.Width = w;
vp.Height = h;
vp.MinDepth = 0.0f;
vp.MaxDepth = 1.0f;
d3d.context->RSSetViewports(1, &vp);
}
static void ApplyScissor(int x, int y, int w, int h, int th)
{
const int top = th - y - h;
const int bottom = th - y;
D3D11_RECT sr;
sr.left = x;
sr.top = top;
sr.right = x + w;
sr.bottom = bottom;
d3d.context->RSSetScissorRects(1, &sr);
}
static void ApplyViewportAndScissor(int x, int y, int w, int h, int th)
{
ApplyViewport(x, y, w, h, th);
ApplyScissor(x, y, w, h, th);
}
static void CreateTexture(Texture* texture, image_t* image, int mipCount, int w, int h)
{
COM_RELEASE(texture->texture);
COM_RELEASE(texture->view);
ID3D11Texture2D* texture2D;
D3D11_TEXTURE2D_DESC texDesc;
ZeroMemory(&texDesc, sizeof(texDesc));
texDesc.ArraySize = 1;
texDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
texDesc.CPUAccessFlags = 0;
texDesc.Format = GetTextureFormat(image->format);
texDesc.Usage = D3D11_USAGE_DEFAULT;
texDesc.Width = w;
texDesc.Height = h;
texDesc.MipLevels = mipCount;
texDesc.SampleDesc.Count = 1;
texDesc.SampleDesc.Quality = 0;
D3D11_CreateTexture2D(&texDesc, NULL, &texture2D, image->name);
ID3D11ShaderResourceView* view;
D3D11_SHADER_RESOURCE_VIEW_DESC viewDesc;
ZeroMemory(&viewDesc, sizeof(viewDesc));
viewDesc.Format = texDesc.Format;
viewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
viewDesc.Texture2D.MipLevels = UINT(-1);
viewDesc.Texture2D.MostDetailedMip = 0;
D3D11_CreateShaderResourceView(texture2D, &viewDesc, &view, image->name);
texture->texture = texture2D;
texture->view = view;
}
static void UpdateAnimatedImage(image_t* image, int w, int h, const byte* data, qbool dirty)
{
if(w != image->width || h != image->height)
{
image->width = w;
image->height = h;
CreateTexture(&d3d.textures[image->texnum], image, 1, w, h);
GAL_UpdateTexture(image, 0, 0, 0, w, h, data);
}
else if(dirty)
{
GAL_UpdateTexture(image, 0, 0, 0, w, h, data);
}
}
static const image_t* GetBundleImage(const textureBundle_t* bundle)
{
return R_UpdateAndGetBundleImage(bundle, &UpdateAnimatedImage);
}
static int ComputeBlendStateIndex(int srcBlend, int dstBlend, qbool alphaToCoverage)
{
return alphaToCoverage * (BLEND_STATE_COUNT * BLEND_STATE_COUNT) + (srcBlend * BLEND_STATE_COUNT) + dstBlend;
}
static void ApplyBlendState(D3D11_BLEND srcBlend, D3D11_BLEND dstBlend, qbool aphaToCoverage)
{
const int index = ComputeBlendStateIndex(srcBlend, dstBlend, aphaToCoverage);
if((unsigned)index >= ARRAY_LEN(d3d.blendStates))
ri.Error(ERR_FATAL, "Tried to set an invalid blend state combo!");
if(d3d.blendStates[index] == NULL)
ri.Error(ERR_FATAL, "Tried to set an unregistered blend state!");
if(index == d3d.blendStateIndex)
{
return;
}
d3d.context->OMSetBlendState(d3d.blendStates[index], NULL, 0xFFFFFFFF);
d3d.blendStateIndex = index;
}
static int ComputeDepthStencilStateIndex(int disableDepth, int funcEqual, int maskTrue)
{
return disableDepth | (funcEqual << 1) | (maskTrue << 2);
}
static void ApplyDepthStencilState(qbool disableDepth, qbool funcEqual, qbool maskTrue)
{
const int index = ComputeDepthStencilStateIndex(disableDepth, funcEqual, maskTrue);
if(index == d3d.depthStencilStateIndex)
{
return;
}
d3d.depthStencilStateIndex = index;
d3d.context->OMSetDepthStencilState(d3d.depthStencilStates[index], 0);
}
static int ComputeRasterizerStateIndex(int wireFrame, int cullType, int polygonOffset)
{
return cullType * 4 + wireFrame * 2 + polygonOffset;
}
static void ApplyRasterizerState(qbool wireFrame, cullType_t cullType, qbool polygonOffset)
{
const int index = ComputeRasterizerStateIndex(wireFrame, cullType, polygonOffset);
if(index == d3d.rasterStateIndex)
{
return;
}
d3d.rasterStateIndex = index;
d3d.context->RSSetState(d3d.rasterStates[index]);
}
static void ApplyState(unsigned int stateBits, cullType_t cullType, qbool polygonOffset)
{
static unsigned int oldStateBits = 0;
const unsigned int diffBits = oldStateBits ^ stateBits;
oldStateBits = stateBits;
d3d.alphaTest = GetAlphaTest(stateBits);
if(diffBits & (GLS_SRCBLEND_BITS | GLS_DSTBLEND_BITS | GLS_ATEST_BITS))
{
const D3D11_BLEND srcBlend = (stateBits & GLS_SRCBLEND_BITS) ? GetSourceBlend(stateBits) : D3D11_BLEND_ONE;
const D3D11_BLEND dstBlend = (stateBits & GLS_DSTBLEND_BITS) ? GetDestinationBlend(stateBits) : D3D11_BLEND_ZERO;
ApplyBlendState(srcBlend, dstBlend, glInfo.alphaToCoverageSupport && d3d.pipelineIndex == PID_GENERIC && d3d.alphaTest != AT_ALWAYS);
}
const qbool disableDepth = (stateBits & GLS_DEPTHTEST_DISABLE) ? 1 : 0;
const qbool funcEqual = (stateBits & GLS_DEPTHFUNC_EQUAL) ? 1 : 0;
const qbool maskTrue = (stateBits & GLS_DEPTHMASK_TRUE) ? 1 : 0;
ApplyDepthStencilState(disableDepth, funcEqual, maskTrue);
// fix up the cull mode for mirrors
if(backEnd.viewParms.isMirror)
{
if(cullType == CT_BACK_SIDED)
{
cullType = CT_FRONT_SIDED;
}
else if(cullType == CT_FRONT_SIDED)
{
cullType = CT_BACK_SIDED;
}
}
ApplyRasterizerState((stateBits & GLS_POLYMODE_LINE) != 0, cullType, polygonOffset);
}
static void BindImage(UINT slot, const image_t* image)
{
ID3D11ShaderResourceView* view = d3d.textures[image->texnum].view;
d3d.context->PSSetShaderResources(slot, 1, &view);
ApplySamplerState(slot, image->wrapClampMode, (image->flags & IMG_NOAF) != 0);
}
static void BindBundle(UINT slot, const textureBundle_t* bundle)
{
BindImage(slot, GetBundleImage(bundle));
}
static void FindBestAvailableAA(DXGI_SAMPLE_DESC* sampleDesc)
{
// @NOTE: D3D10_MAX_MULTISAMPLE_SAMPLE_COUNT == D3D11_MAX_MULTISAMPLE_SAMPLE_COUNT
sampleDesc->Count = (UINT)min(r_msaa->integer, D3D11_MAX_MULTISAMPLE_SAMPLE_COUNT);
sampleDesc->Quality = 0;
while(sampleDesc->Count > 0)
{
UINT levelCount = 0;
if(SUCCEEDED(d3d.device->CheckMultisampleQualityLevels(DXGI_FORMAT_R8G8B8A8_UNORM, sampleDesc->Count, &levelCount)) &&
levelCount > 0)
break;
--sampleDesc->Count;
}
if(sampleDesc->Count <= 1)
{
sampleDesc->Count = 1;
sampleDesc->Quality = 0;
}
}
static qbool GAL_Init()
{
Sys_V_Init(GAL_D3D11);
ZeroMemory(&d3d, sizeof(d3d));
d3d.library = LoadLibraryA("D3D11.dll");
if(d3d.library == NULL)
ri.Error(ERR_FATAL, "D3D11.dll couldn't be found or opened");
PFN_D3D11_CREATE_DEVICE_AND_SWAP_CHAIN pD3D11CreateDeviceAndSwapChain =
(PFN_D3D11_CREATE_DEVICE_AND_SWAP_CHAIN)GetProcAddress(d3d.library, "D3D11CreateDeviceAndSwapChain");
if(pD3D11CreateDeviceAndSwapChain == NULL)
ri.Error(ERR_FATAL, "Failed to locate D3D11CreateDeviceAndSwapChain in D3D11.dll");
const D3D_FEATURE_LEVEL featureLevels[2] = { D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_1 };
UINT flags = D3D11_CREATE_DEVICE_SINGLETHREADED;
#if defined(_DEBUG)
flags |= D3D11_CREATE_DEVICE_DEBUG;
#endif
DXGI_SWAP_CHAIN_DESC swapChainDesc;
ZeroMemory(&swapChainDesc, sizeof(swapChainDesc));
swapChainDesc.BufferCount = 1;
swapChainDesc.BufferDesc.Width = glInfo.winWidth;
swapChainDesc.BufferDesc.Height = glInfo.winHeight;
swapChainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
swapChainDesc.BufferDesc.RefreshRate.Numerator = 0;
swapChainDesc.BufferDesc.RefreshRate.Denominator = 1;
swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
swapChainDesc.OutputWindow = GetActiveWindow();
swapChainDesc.SampleDesc.Count = 1;
swapChainDesc.SampleDesc.Quality = 0;
swapChainDesc.Windowed = TRUE;
swapChainDesc.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
swapChainDesc.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
swapChainDesc.Flags = 0;
create_device:
HRESULT hr = (*pD3D11CreateDeviceAndSwapChain)(
NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, flags, featureLevels, ARRAY_LEN(featureLevels), D3D11_SDK_VERSION,
&swapChainDesc, &d3d.swapChain, &d3d.device, NULL, &d3d.context);
if(hr == DXGI_ERROR_SDK_COMPONENT_MISSING)
{
ri.Printf(PRINT_WARNING, "D3D11CreateDeviceAndSwapChain failed because you don't have the SDK installed.\n");
ri.Printf(PRINT_WARNING, "Trying to create the device again without the debug layer...\n");
flags &= ~D3D11_CREATE_DEVICE_DEBUG;
goto create_device;
}
Check(hr, "D3D11CreateDeviceAndSwapChain");
D3D11_TEXTURE2D_DESC readbackTexDesc;
ZeroMemory(&readbackTexDesc, sizeof(readbackTexDesc));
readbackTexDesc.Width = glConfig.vidWidth;
readbackTexDesc.Height = glConfig.vidHeight;
readbackTexDesc.MipLevels = 1;
readbackTexDesc.ArraySize = 1;
readbackTexDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
readbackTexDesc.SampleDesc.Count = 1;
readbackTexDesc.SampleDesc.Quality = 0;
readbackTexDesc.Usage = D3D11_USAGE_STAGING;
readbackTexDesc.BindFlags = 0;
readbackTexDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
readbackTexDesc.MiscFlags = 0;
d3d.errorMode = EM_SILENT;
if(!D3D11_CreateTexture2D(&readbackTexDesc, 0, &d3d.readbackTexture, "screenshot/video readback texture"))
ri.Printf(PRINT_WARNING, "Screengrab texture creation failed! /" S_COLOR_CMD "screenshot^7* and /" S_COLOR_CMD "video^7 won't work\n");
d3d.errorMode = EM_FATAL;
hr = d3d.swapChain->GetBuffer(0, IID_ID3D11Texture2D, (void**)&d3d.backBufferTexture);
CheckAndName(hr, "GetBuffer", d3d.backBufferTexture, "back buffer texture");
D3D11_RENDER_TARGET_VIEW_DESC colorViewDesc; // needed?
ZeroMemory(&colorViewDesc, sizeof(colorViewDesc));
colorViewDesc.Format = swapChainDesc.BufferDesc.Format;
colorViewDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
colorViewDesc.Texture2D.MipSlice = 0;
D3D11_CreateRenderTargetView(d3d.backBufferTexture, &colorViewDesc, &d3d.backBufferRTView, "back buffer render target view");
DXGI_SAMPLE_DESC sampleDesc;
FindBestAvailableAA(&sampleDesc);
const qbool alphaToCoverageOK = sampleDesc.Count > 1 && r_alphaToCoverage->integer != 0;
D3D11_TEXTURE2D_DESC renderTargetTexDesc;
ZeroMemory(&renderTargetTexDesc, sizeof(renderTargetTexDesc));
renderTargetTexDesc.Width = glConfig.vidWidth;
renderTargetTexDesc.Height = glConfig.vidHeight;
renderTargetTexDesc.MipLevels = 1;
renderTargetTexDesc.ArraySize = 1;
renderTargetTexDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
renderTargetTexDesc.SampleDesc.Count = sampleDesc.Count;
renderTargetTexDesc.SampleDesc.Quality = sampleDesc.Quality;
renderTargetTexDesc.Usage = D3D11_USAGE_DEFAULT;
renderTargetTexDesc.BindFlags = D3D11_BIND_RENDER_TARGET;
renderTargetTexDesc.CPUAccessFlags = 0;
renderTargetTexDesc.MiscFlags = 0;
D3D11_CreateTexture2D(&renderTargetTexDesc, 0, &d3d.renderTargetTextureMS, "MS render target texture");
D3D11_RENDER_TARGET_VIEW_DESC rtvDesc;
ZeroMemory(&rtvDesc, sizeof(rtvDesc));
rtvDesc.Format = renderTargetTexDesc.Format;
rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DMS;
D3D11_CreateRenderTargetView(d3d.renderTargetTextureMS, &rtvDesc, &d3d.renderTargetViewMS, "MS render target view");
ZeroMemory(&renderTargetTexDesc, sizeof(renderTargetTexDesc));
renderTargetTexDesc.Width = glConfig.vidWidth;
renderTargetTexDesc.Height = glConfig.vidHeight;
renderTargetTexDesc.MipLevels = 1;
renderTargetTexDesc.ArraySize = 1;
renderTargetTexDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
renderTargetTexDesc.SampleDesc.Count = 1;
renderTargetTexDesc.SampleDesc.Quality = 0;
renderTargetTexDesc.Usage = D3D11_USAGE_DEFAULT;
renderTargetTexDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
renderTargetTexDesc.CPUAccessFlags = 0;
renderTargetTexDesc.MiscFlags = 0;
D3D11_CreateTexture2D(&renderTargetTexDesc, 0, &d3d.resolveTexture, "resolve texture");
D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc;
ZeroMemory(&srvDesc, sizeof(srvDesc));
srvDesc.Format = renderTargetTexDesc.Format;
srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
srvDesc.Texture2D.MipLevels = 1;
srvDesc.Texture2D.MostDetailedMip = 0;
D3D11_CreateShaderResourceView(d3d.resolveTexture, &srvDesc, &d3d.resolveTextureShaderView, "resolve texture shader resource view");
D3D11_TEXTURE2D_DESC depthStencilTexDesc;
ZeroMemory(&depthStencilTexDesc, sizeof(depthStencilTexDesc));
depthStencilTexDesc.Width = glConfig.vidWidth;
depthStencilTexDesc.Height = glConfig.vidHeight;
depthStencilTexDesc.MipLevels = 1;
depthStencilTexDesc.ArraySize = 1;
//depthStencilTexDesc.Format = DXGI_FORMAT_R32_TYPELESS; // DXGI_FORMAT_R24G8_TYPELESS
depthStencilTexDesc.Format = DXGI_FORMAT_R24G8_TYPELESS;
depthStencilTexDesc.SampleDesc.Count = sampleDesc.Count;
depthStencilTexDesc.SampleDesc.Quality = sampleDesc.Quality;
depthStencilTexDesc.Usage = D3D11_USAGE_DEFAULT;
depthStencilTexDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL | D3D11_BIND_SHADER_RESOURCE;
depthStencilTexDesc.CPUAccessFlags = 0;
depthStencilTexDesc.MiscFlags = 0;
D3D11_CreateTexture2D(&depthStencilTexDesc, 0, &d3d.depthStencilTexture, "depth stencil texture");
D3D11_DEPTH_STENCIL_VIEW_DESC depthStencilViewDesc;
ZeroMemory(&depthStencilViewDesc, sizeof(depthStencilViewDesc));
//depthStencilViewDesc.Format = DXGI_FORMAT_D32_FLOAT; // DXGI_FORMAT_D24_UNORM_S8_UINT
depthStencilViewDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
depthStencilViewDesc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2DMS;
depthStencilViewDesc.Texture2D.MipSlice = 0;
hr = d3d.device->CreateDepthStencilView(d3d.depthStencilTexture, &depthStencilViewDesc, &d3d.depthStencilView);
CheckAndName(hr, "CreateDepthStencilView", d3d.depthStencilView, "depth stencil view");
ZeroMemory(&srvDesc, sizeof(srvDesc));
//srvDesc.Format = DXGI_FORMAT_R32_FLOAT; // DXGI_FORMAT_R24_UNORM_X8_TYPELESS
srvDesc.Format = DXGI_FORMAT_R24_UNORM_X8_TYPELESS;
srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DMS;
D3D11_CreateShaderResourceView(d3d.depthStencilTexture, &srvDesc, &d3d.depthStencilShaderView, "depth stencil shader resource view");
const ShaderDesc* const genericPS = &genericPixelShaders[(alphaToCoverageOK != 0) + 2 * (r_dither->integer != 0)];
D3D11_CreateVertexShader(g_generic_vs, ARRAY_LEN(g_generic_vs), NULL, &d3d.pipelines[PID_GENERIC].vertexShader, "generic vertex shader");
D3D11_CreatePixelShader(genericPS->code, genericPS->size, NULL, &d3d.pipelines[PID_GENERIC].pixelShader, genericPS->name);
D3D11_INPUT_ELEMENT_DESC genericInputLayoutDesc[] =
{
{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
{ "COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 1, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
{ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 2, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
{ "TEXCOORD", 1, DXGI_FORMAT_R32G32_FLOAT, 3, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 }
};
D3D11_CreateInputLayout(genericInputLayoutDesc, ARRAY_LEN(genericInputLayoutDesc), g_generic_vs, ARRAY_LEN(g_generic_vs), &d3d.pipelines[PID_GENERIC].inputLayout, "generic input layout");
d3d.context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
const int maxVertexCount = SHADER_MAX_VERTEXES;
const int maxIndexCount = SHADER_MAX_INDEXES;
VertexBuffer* const vb = d3d.vertexBuffers;
vb[VB_POSITION].itemSize = sizeof(vec4_t);
vb[VB_NORMAL].itemSize = sizeof(vec4_t);
vb[VB_TEXCOORD].itemSize = sizeof(vec2_t);
vb[VB_TEXCOORD2].itemSize = sizeof(vec2_t);
vb[VB_COLOR].itemSize = sizeof(color4ub_t);
d3d.indexBuffer.itemSize = sizeof(uint32_t);
for(int i = 0; i < ARRAY_LEN(d3d.vertexBuffers); ++i)
{
vb[i].capacity = maxVertexCount;
vb[i].discard = qtrue;
}
d3d.indexBuffer.capacity = maxIndexCount;
d3d.indexBuffer.discard = qtrue;
D3D11_BUFFER_DESC vertexBufferDesc;
ZeroMemory(&vertexBufferDesc, sizeof(vertexBufferDesc));
vertexBufferDesc.Usage = D3D11_USAGE_DYNAMIC;
vertexBufferDesc.ByteWidth = maxVertexCount * vb[VB_POSITION].itemSize;
vertexBufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
vertexBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
D3D11_CreateBuffer(&vertexBufferDesc, NULL, &vb[VB_POSITION].buffer, "position vertex buffer");
D3D11_CreateBuffer(&vertexBufferDesc, NULL, &vb[VB_NORMAL].buffer, "normal vertex buffer");
D3D11_BUFFER_DESC colorBufferDesc;
ZeroMemory(&colorBufferDesc, sizeof(colorBufferDesc));
colorBufferDesc.Usage = D3D11_USAGE_DYNAMIC;
colorBufferDesc.ByteWidth = maxVertexCount * vb[VB_COLOR].itemSize;
colorBufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
colorBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
D3D11_CreateBuffer(&colorBufferDesc, NULL, &vb[VB_COLOR].buffer, "color vertex buffer");
D3D11_BUFFER_DESC texCoordBufferDesc;
ZeroMemory(&texCoordBufferDesc, sizeof(texCoordBufferDesc));
texCoordBufferDesc.Usage = D3D11_USAGE_DYNAMIC;
texCoordBufferDesc.ByteWidth = maxVertexCount * vb[VB_TEXCOORD].itemSize;
texCoordBufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
texCoordBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
D3D11_CreateBuffer(&texCoordBufferDesc, NULL, &vb[VB_TEXCOORD].buffer, "texture coordinates vertex buffer #1");
D3D11_CreateBuffer(&texCoordBufferDesc, NULL, &vb[VB_TEXCOORD2].buffer, "texture coordinates vertex buffer #2");
D3D11_BUFFER_DESC indexBufferDesc;
ZeroMemory(&indexBufferDesc, sizeof(indexBufferDesc));
indexBufferDesc.Usage = D3D11_USAGE_DYNAMIC;
indexBufferDesc.ByteWidth = maxIndexCount * d3d.indexBuffer.itemSize;
indexBufferDesc.BindFlags = D3D11_BIND_INDEX_BUFFER;
indexBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
D3D11_CreateBuffer(&indexBufferDesc, NULL, &d3d.indexBuffer.buffer, "index buffer");
d3d.context->IASetIndexBuffer(d3d.indexBuffer.buffer, DXGI_FORMAT_R32_UINT, 0);
D3D11_BUFFER_DESC vertexShaderBufferDesc;
ZeroMemory(&vertexShaderBufferDesc, sizeof(vertexShaderBufferDesc));
vertexShaderBufferDesc.Usage = D3D11_USAGE_DYNAMIC;
vertexShaderBufferDesc.ByteWidth = sizeof(GenericVSData);
vertexShaderBufferDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
vertexShaderBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
D3D11_CreateBuffer(&vertexShaderBufferDesc, NULL, &d3d.pipelines[PID_GENERIC].vertexBuffer, "generic vertex shader buffer");
D3D11_BUFFER_DESC pixelShaderBufferDesc;
ZeroMemory(&pixelShaderBufferDesc, sizeof(pixelShaderBufferDesc));
pixelShaderBufferDesc.Usage = D3D11_USAGE_DYNAMIC;
pixelShaderBufferDesc.ByteWidth = sizeof(GenericPSData);
pixelShaderBufferDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
pixelShaderBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
D3D11_CreateBuffer(&pixelShaderBufferDesc, NULL, &d3d.pipelines[PID_GENERIC].pixelBuffer, "generic pixel shader buffer");
// create all sampler states
for(int bilinear = 0; bilinear < 2; ++bilinear)
{
for(int wrapMode = 0; wrapMode < TW_COUNT; ++wrapMode)
{
const int index = ComputeSamplerStateIndex(wrapMode, bilinear);
// @NOTE: D3D10_REQ_MAXANISOTROPY == D3D11_REQ_MAXANISOTROPY
const int maxAnisotropy = Com_ClampInt(1, D3D11_REQ_MAXANISOTROPY, r_ext_max_anisotropy->integer);
const D3D11_TEXTURE_ADDRESS_MODE mode = GetTextureAddressMode((textureWrap_t)wrapMode);
ID3D11SamplerState* samplerState;
D3D11_SAMPLER_DESC samplerDesc;
ZeroMemory(&samplerDesc, sizeof(samplerDesc));
samplerDesc.Filter = (bilinear || maxAnisotropy == 1) ? D3D11_FILTER_MIN_MAG_MIP_LINEAR : D3D11_FILTER_ANISOTROPIC;
samplerDesc.AddressU = mode;
samplerDesc.AddressV = mode;
samplerDesc.AddressW = mode;
samplerDesc.ComparisonFunc = D3D11_COMPARISON_NEVER;
samplerDesc.MinLOD = -D3D11_FLOAT32_MAX;
samplerDesc.MaxLOD = D3D11_FLOAT32_MAX;
samplerDesc.MaxAnisotropy = bilinear ? 1 : maxAnisotropy;
hr = d3d.device->CreateSamplerState(&samplerDesc, &samplerState);
CheckAndName(hr, "CreateSamplerState", samplerState, va("sampler state %d", index));
d3d.samplerStates[index] = samplerState;
}
}
// force set the default sampler states
for(int i = 0; i < ARRAY_LEN(d3d.samplerStateIndices); ++i)
{
d3d.samplerStateIndices[i] = -1;
ApplySamplerState(i, TW_CLAMP_TO_EDGE, qtrue);
}
// create all blend states
const int coverageModes = alphaToCoverageOK ? 2 : 1;
for(int a = 0; a < coverageModes; ++a)
{
for(int s = 1; s < BLEND_STATE_COUNT; ++s)
{
for(int d = 1; d < BLEND_STATE_COUNT; ++d)
{
const int index = ComputeBlendStateIndex(s, d, a);
ID3D11BlendState* blendState;
D3D11_BLEND_DESC blendDesc;
ZeroMemory(&blendDesc, sizeof(blendDesc));
blendDesc.AlphaToCoverageEnable = a == 1 ? TRUE : FALSE;
blendDesc.RenderTarget[0].BlendEnable = TRUE;
blendDesc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
blendDesc.RenderTarget[0].SrcBlend = (D3D11_BLEND)s;
blendDesc.RenderTarget[0].DestBlend = (D3D11_BLEND)d;
blendDesc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
blendDesc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE;
blendDesc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ZERO;
blendDesc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL;
hr = d3d.device->CreateBlendState(&blendDesc, &blendState);
CheckAndName(hr, "CreateBlendState", blendState, va("blend state %d", index));
d3d.blendStates[index] = blendState;
}
}
}
// create all the depth/stencil states
for(int disableDepth = 0; disableDepth < 2; ++disableDepth)
{
for(int funcEqual = 0; funcEqual < 2; ++funcEqual)
{
for(int maskTrue = 0; maskTrue < 2; ++maskTrue)
{
const int index = ComputeDepthStencilStateIndex(disableDepth, funcEqual, maskTrue);
ID3D11DepthStencilState* depthState;
D3D11_DEPTH_STENCIL_DESC depthDesc;
ZeroMemory(&depthDesc, sizeof(depthDesc));
depthDesc.DepthEnable = disableDepth ? FALSE : TRUE;
depthDesc.DepthFunc = funcEqual ? D3D11_COMPARISON_EQUAL : D3D11_COMPARISON_LESS_EQUAL;
depthDesc.DepthWriteMask = maskTrue ? D3D11_DEPTH_WRITE_MASK_ALL : D3D11_DEPTH_WRITE_MASK_ZERO;
depthDesc.StencilEnable = FALSE;
hr = d3d.device->CreateDepthStencilState(&depthDesc, &depthState);
CheckAndName(hr, "CreateDepthStencilState", depthState, va("depth/stencil state %d", index));
d3d.depthStencilStates[index] = depthState;
}
}
}
// create all the raster states
for(int polygonOffset = 0; polygonOffset < 2; ++polygonOffset)
{
for(int wireFrame = 0; wireFrame < 2; ++wireFrame)
{
for(int cullType = 0; cullType < CT_COUNT; ++cullType)
{
const int index = ComputeRasterizerStateIndex(wireFrame, cullType, polygonOffset);
ID3D11RasterizerState* rasterState;
D3D11_RASTERIZER_DESC rasterDesc;
ZeroMemory(&rasterDesc, sizeof(rasterDesc));
rasterDesc.FillMode = wireFrame ? D3D11_FILL_WIREFRAME : D3D11_FILL_SOLID;
rasterDesc.CullMode = GetCullMode((cullType_t)cullType);
rasterDesc.FrontCounterClockwise = TRUE;
rasterDesc.ScissorEnable = TRUE;
rasterDesc.DepthClipEnable = FALSE;
rasterDesc.DepthBiasClamp = 0.0f;
rasterDesc.DepthBias = polygonOffset ? -1 : 0;
rasterDesc.SlopeScaledDepthBias = polygonOffset ? -1.0f : 0.0f;
hr = d3d.device->CreateRasterizerState(&rasterDesc, &rasterState);
CheckAndName(hr, "CreateRasterizerState", rasterState, va("raster state %d", index));
d3d.rasterStates[index] = rasterState;
}
}
}
//
// post-processing
//
D3D11_CreateVertexShader(g_post_vs, ARRAY_LEN(g_post_vs), NULL, &d3d.pipelines[PID_POST_PROCESS].vertexShader, "post-process vertex shader");
D3D11_CreatePixelShader(g_post_ps, ARRAY_LEN(g_post_ps), NULL, &d3d.pipelines[PID_POST_PROCESS].pixelShader, "post-process pixel shader");
ZeroMemory(&vertexShaderBufferDesc, sizeof(vertexShaderBufferDesc));
vertexShaderBufferDesc.Usage = D3D11_USAGE_DYNAMIC;
vertexShaderBufferDesc.ByteWidth = sizeof(d3d.postVSData);
vertexShaderBufferDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
vertexShaderBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
D3D11_CreateBuffer(&vertexShaderBufferDesc, NULL, &d3d.pipelines[PID_POST_PROCESS].vertexBuffer, "post-process vertex shader buffer");
ZeroMemory(&pixelShaderBufferDesc, sizeof(pixelShaderBufferDesc));
pixelShaderBufferDesc.Usage = D3D11_USAGE_DYNAMIC;
pixelShaderBufferDesc.ByteWidth = sizeof(d3d.postPSData);
pixelShaderBufferDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
pixelShaderBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
D3D11_CreateBuffer(&pixelShaderBufferDesc, NULL, &d3d.pipelines[PID_POST_PROCESS].pixelBuffer, "post-process pixel shader buffer");
//
// dynamic lights
//
D3D11_CreateVertexShader(g_dl_vs, ARRAY_LEN(g_dl_vs), NULL, &d3d.pipelines[PID_DYNAMIC_LIGHT].vertexShader, "dynamic light vertex shader");
D3D11_CreatePixelShader(g_dl_ps, ARRAY_LEN(g_dl_ps), NULL, &d3d.pipelines[PID_DYNAMIC_LIGHT].pixelShader, "dynamic light pixel shader");
D3D11_INPUT_ELEMENT_DESC dlInputLayoutDesc[] =
{
{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
{ "NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 1, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
{ "COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 2, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
{ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 3, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 }
};
D3D11_CreateInputLayout(dlInputLayoutDesc, ARRAY_LEN(dlInputLayoutDesc), g_dl_vs, ARRAY_LEN(g_dl_vs), &d3d.pipelines[PID_DYNAMIC_LIGHT].inputLayout, "dynamic light input layout");
ZeroMemory(&vertexShaderBufferDesc, sizeof(vertexShaderBufferDesc));
vertexShaderBufferDesc.Usage = D3D11_USAGE_DYNAMIC;
vertexShaderBufferDesc.ByteWidth = sizeof(DynamicLightVSData);
vertexShaderBufferDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
vertexShaderBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
D3D11_CreateBuffer(&vertexShaderBufferDesc, NULL, &d3d.pipelines[PID_DYNAMIC_LIGHT].vertexBuffer, "dynamic light vertex shader buffer");
ZeroMemory(&pixelShaderBufferDesc, sizeof(pixelShaderBufferDesc));
pixelShaderBufferDesc.Usage = D3D11_USAGE_DYNAMIC;
pixelShaderBufferDesc.ByteWidth = sizeof(DynamicLightPSData);
pixelShaderBufferDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
pixelShaderBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
D3D11_CreateBuffer(&pixelShaderBufferDesc, NULL, &d3d.pipelines[PID_DYNAMIC_LIGHT].pixelBuffer, "dynamic light pixel shader buffer");
//
// soft sprites
//
D3D11_CreateVertexShader(g_sprite_vs, ARRAY_LEN(g_sprite_vs), NULL, &d3d.pipelines[PID_SOFT_SPRITE].vertexShader, "soft sprite vertex shader");
D3D11_CreatePixelShader(g_sprite_ps, ARRAY_LEN(g_sprite_ps), NULL, &d3d.pipelines[PID_SOFT_SPRITE].pixelShader, "soft sprite pixel shader");
D3D11_INPUT_ELEMENT_DESC ssInputLayoutDesc[] =
{
{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
{ "COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 1, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
{ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 2, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 }
};
D3D11_CreateInputLayout(ssInputLayoutDesc, ARRAY_LEN(ssInputLayoutDesc), g_sprite_vs, ARRAY_LEN(g_sprite_vs), &d3d.pipelines[PID_SOFT_SPRITE].inputLayout, "soft sprite input layout");
ZeroMemory(&vertexShaderBufferDesc, sizeof(vertexShaderBufferDesc));
vertexShaderBufferDesc.Usage = D3D11_USAGE_DYNAMIC;
vertexShaderBufferDesc.ByteWidth = sizeof(SoftSpriteVSData);
vertexShaderBufferDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
vertexShaderBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
D3D11_CreateBuffer(&vertexShaderBufferDesc, NULL, &d3d.pipelines[PID_SOFT_SPRITE].vertexBuffer, "soft sprite vertex shader buffer");
ZeroMemory(&pixelShaderBufferDesc, sizeof(pixelShaderBufferDesc));
pixelShaderBufferDesc.Usage = D3D11_USAGE_DYNAMIC;
pixelShaderBufferDesc.ByteWidth = sizeof(SoftSpritePSData);
pixelShaderBufferDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
pixelShaderBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
D3D11_CreateBuffer(&pixelShaderBufferDesc, NULL, &d3d.pipelines[PID_SOFT_SPRITE].pixelBuffer, "soft sprite pixel shader buffer");
//
// mip-map generation
//
qbool mipGenOK = qfalse;
if(r_gpuMipGen->integer && d3d.device->GetFeatureLevel() == D3D_FEATURE_LEVEL_11_0)
{
d3d.errorMode = EM_SILENT;
mipGenOK = qtrue;
mipGenOK &= D3D11_CreateComputeShader(g_mip_pass_cs, ARRAY_LEN(g_mip_pass_cs), NULL, &d3d.mipDownSampleComputeShader, "mip-map down-sampling compute shader");
mipGenOK &= D3D11_CreateComputeShader(g_mip_start_cs, ARRAY_LEN(g_mip_start_cs), NULL, &d3d.mipGammaToLinearComputeShader, "gamma-to-linear compute shader");
mipGenOK &= D3D11_CreateComputeShader(g_mip_end_cs, ARRAY_LEN(g_mip_end_cs), NULL, &d3d.mipLinearToGammaComputeShader, "linear-to-gamma compute shader");
D3D11_BUFFER_DESC bufferDesc;
ZeroMemory(&bufferDesc, sizeof(bufferDesc));
bufferDesc.Usage = D3D11_USAGE_DYNAMIC;
bufferDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
bufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
bufferDesc.ByteWidth = sizeof(Down4CSData);
mipGenOK &= D3D11_CreateBuffer(&bufferDesc, NULL, &d3d.mipDownSampleConstBuffer, "mip-map down-sampling compute shader buffer");
bufferDesc.ByteWidth = sizeof(LinearToGammaCSData);
mipGenOK &= D3D11_CreateBuffer(&bufferDesc, NULL, &d3d.mipLinearToGammaConstBuffer, "mip-map linear-to-gamma compute shader buffer");
bufferDesc.ByteWidth = sizeof(GammaToLinearCSData);
mipGenOK &= D3D11_CreateBuffer(&bufferDesc, NULL, &d3d.mipGammaToLinearConstBuffer, "mip-map gamma-to-linear compute shader buffer");
for(int i = 0; i < ARRAY_LEN(d3d.mipGenTextures); ++i)
{
D3D11_TEXTURE2D_DESC textureDesc;
ZeroMemory(&textureDesc, sizeof(textureDesc));
textureDesc.Width = MAX_GPU_TEXTURE_SIZE;
textureDesc.Height = MAX_GPU_TEXTURE_SIZE;
textureDesc.MipLevels = 1;
textureDesc.ArraySize = 1;
textureDesc.Format = i == 2 ? DXGI_FORMAT_R8G8B8A8_UINT : DXGI_FORMAT_R16G16B16A16_FLOAT;
textureDesc.SampleDesc.Count = 1;
textureDesc.SampleDesc.Quality = 0;
textureDesc.Usage = D3D11_USAGE_DEFAULT;
textureDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_UNORDERED_ACCESS;
textureDesc.CPUAccessFlags = 0;
textureDesc.MiscFlags = 0;
mipGenOK &= D3D11_CreateTexture2D(&textureDesc, 0, &d3d.mipGenTextures[i].texture, va("mip-map generation texture #%d", i + 1));
D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc;
ZeroMemory(&srvDesc, sizeof(srvDesc));
srvDesc.Format = textureDesc.Format;
srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
srvDesc.Texture2D.MipLevels = 1;
srvDesc.Texture2D.MostDetailedMip = 0;
mipGenOK &= D3D11_CreateShaderResourceView(d3d.mipGenTextures[i].texture, &srvDesc, &d3d.mipGenTextures[i].srv, va("mip-map generation SRV #%d", i + 1));
D3D11_UNORDERED_ACCESS_VIEW_DESC uavDesc;
ZeroMemory(&uavDesc, sizeof(uavDesc));
uavDesc.Format = textureDesc.Format;
uavDesc.ViewDimension = D3D11_UAV_DIMENSION_TEXTURE2D;
uavDesc.Texture2D.MipSlice = 0;
mipGenOK &= D3D11_CreateUnorderedAccessView(d3d.mipGenTextures[i].texture, &uavDesc, &d3d.mipGenTextures[i].uav, va("mip-map generation SRV #%d", i + 1));
}
d3d.errorMode = EM_FATAL;
}
//
// misc.
//
// select the generic pipeline to begin with
d3d.pipelineIndex = (PipelineId)-1;
ApplyPipeline(PID_GENERIC);
// force set all the default non-sampler states
d3d.blendStateIndex = -1;
d3d.depthStencilStateIndex = -1;
d3d.rasterStateIndex = -1;
ApplyState(GLS_DEFAULT, CT_TWO_SIDED, qfalse);
glConfig.colorBits = 32;
glConfig.depthBits = 24;
glConfig.stencilBits = 8;
glConfig.unused_maxTextureSize = MAX_GPU_TEXTURE_SIZE;
glConfig.unused_maxActiveTextures = 0;
glConfig.unused_driverType = 0; // ICD
glConfig.unused_hardwareType = 0; // generic
glConfig.unused_deviceSupportsGamma = qtrue;
glConfig.unused_textureCompression = 0; // no compression
glConfig.unused_textureEnvAddAvailable = qtrue;
glConfig.unused_displayFrequency = 0;
glConfig.unused_isFullscreen = !!r_fullscreen->integer;
glConfig.unused_stereoEnabled = qfalse;
glConfig.unused_smpActive = qfalse;
glConfig.extensions_string[0] = '\0';
glConfig.renderer_string[0] = '\0';
glConfig.vendor_string[0] = '\0';
glConfig.version_string[0] = '\0';
glInfo.displayFrequency = 0;
glInfo.maxAnisotropy = D3D11_REQ_MAXANISOTROPY; // @NOTE: D3D10_REQ_MAXANISOTROPY == D3D11_REQ_MAXANISOTROPY
glInfo.maxTextureSize = MAX_GPU_TEXTURE_SIZE;
glInfo.softSpriteSupport = qtrue;
glInfo.mipGenSupport = mipGenOK;
glInfo.alphaToCoverageSupport = alphaToCoverageOK;
IDXGIDevice* dxgiDevice;
if(SUCCEEDED(d3d.device->QueryInterface(__uuidof(IDXGIDevice), (void**)&dxgiDevice)))
{
IDXGIAdapter* dxgiAdapter;
if(SUCCEEDED(dxgiDevice->GetAdapter(&dxgiAdapter)))
{
DXGI_ADAPTER_DESC desc;
if(SUCCEEDED(dxgiAdapter->GetDesc(&desc)))
{
char name[ARRAY_LEN(desc.Description) + 1];
if(WideCharToMultiByte(CP_UTF7, 0, desc.Description, -1, name, sizeof(name) - 1, NULL, NULL) > 0)
{
Q_strncpyz(glConfig.renderer_string, name, sizeof(glConfig.renderer_string));
}
d3d.adapterInfo.valid = qtrue;
d3d.adapterInfo.dedicatedSystemMemoryMB = (int)(desc.DedicatedSystemMemory >> 20);
d3d.adapterInfo.dedicatedVideoMemoryMB = (int)(desc.DedicatedVideoMemory >> 20);
d3d.adapterInfo.sharedSystemMemoryMB = (int)(desc.SharedSystemMemory >> 20);
}
}
COM_RELEASE(dxgiDevice);
}
qbool maxFrameLatencySet = qfalse;
IDXGIDevice1* dxgiDevice1;
if(SUCCEEDED(d3d.device->QueryInterface(__uuidof(IDXGIDevice1), (void**)&dxgiDevice1)))
{
if(SUCCEEDED(dxgiDevice1->SetMaximumFrameLatency(r_d3d11_maxQueuedFrames->integer)))
{
maxFrameLatencySet = qtrue;
}
COM_RELEASE(dxgiDevice1);
}
if(maxFrameLatencySet)
{
ri.Printf(PRINT_ALL, "Max. queued frames: %d\n", r_d3d11_maxQueuedFrames->integer);
}
else
{
ri.Printf(PRINT_ERROR, "Failed to set the max. number of queued frames\n");
}
if(r_d3d11_syncOffsets->integer == D3D11SO_AUTO)
{
// only nVidia's drivers seem to consistently handle the extra IASetVertexBuffers calls well enough
d3d.splitBufferOffsets = Q_stristr(glConfig.renderer_string, "NVIDIA") != NULL;
}
else
{
d3d.splitBufferOffsets = r_d3d11_syncOffsets->integer == D3D11SO_SPLITOFFSETS;
}
ri.Printf(PRINT_ALL, "MSAA: %d samples requested, %d selected\n", r_msaa->integer, sampleDesc.Count);
return qtrue;
}
static void GAL_ShutDown(qbool fullShutDown)
{
for(int i = 0; i < d3d.textureCount; ++i)
{
COM_RELEASE(d3d.textures[i].view);
COM_RELEASE(d3d.textures[i].texture);
}
for(int i = 0; i < ARRAY_LEN(d3d.pipelines); ++i)
{
COM_RELEASE(d3d.pipelines[i].inputLayout);
COM_RELEASE(d3d.pipelines[i].vertexShader);
COM_RELEASE(d3d.pipelines[i].pixelShader);
COM_RELEASE(d3d.pipelines[i].vertexBuffer);
COM_RELEASE(d3d.pipelines[i].pixelBuffer);
}
for(int i = 0; i < ARRAY_LEN(d3d.mipGenTextures); ++i)
{
COM_RELEASE(d3d.mipGenTextures[i].texture);
COM_RELEASE(d3d.mipGenTextures[i].srv);
COM_RELEASE(d3d.mipGenTextures[i].uav);
}
for(int i = 0; i < ARRAY_LEN(d3d.vertexBuffers); ++i)
{
COM_RELEASE(d3d.vertexBuffers[i].buffer);
}
COM_RELEASE(d3d.indexBuffer.buffer);
COM_RELEASE_ARRAY(d3d.samplerStates);
COM_RELEASE_ARRAY(d3d.blendStates);
COM_RELEASE_ARRAY(d3d.depthStencilStates);
COM_RELEASE_ARRAY(d3d.rasterStates);
COM_RELEASE(d3d.backBufferTexture);
COM_RELEASE(d3d.backBufferRTView);
COM_RELEASE(d3d.renderTargetTextureMS);
COM_RELEASE(d3d.renderTargetViewMS);
COM_RELEASE(d3d.resolveTexture);
COM_RELEASE(d3d.resolveTextureShaderView);
COM_RELEASE(d3d.depthStencilTexture);
COM_RELEASE(d3d.depthStencilView);
COM_RELEASE(d3d.depthStencilShaderView);
COM_RELEASE(d3d.readbackTexture);
COM_RELEASE(d3d.mipGammaToLinearComputeShader);
COM_RELEASE(d3d.mipLinearToGammaComputeShader);
COM_RELEASE(d3d.mipDownSampleComputeShader);
COM_RELEASE(d3d.mipDownSampleConstBuffer);
COM_RELEASE(d3d.mipLinearToGammaConstBuffer);
COM_RELEASE(d3d.mipGammaToLinearConstBuffer);
COM_RELEASE(d3d.device);
COM_RELEASE(d3d.context);
COM_RELEASE(d3d.swapChain);
for(int i = 0; i < ARRAY_LEN(d3d.frameQueries); ++i)
{
COM_RELEASE(d3d.frameQueries[i].disjoint);
COM_RELEASE(d3d.frameQueries[i].frameStart);
COM_RELEASE(d3d.frameQueries[i].frameEnd);
}
if(d3d.library)
FreeLibrary(d3d.library);
memset(&d3d, 0, sizeof(d3d));
tr.numImages = 0;
memset(tr.images, 0, sizeof(tr.images));
}
static void BeginQueries()
{
FrameQueries* const queries = &d3d.frameQueries[d3d.frameQueriesWriteIndex];
queries->valid = qfalse;
COM_RELEASE(queries->disjoint);
COM_RELEASE(queries->frameStart);
COM_RELEASE(queries->frameEnd);
D3D11_QUERY_DESC qd;
qd.MiscFlags = 0;
qd.Query = D3D11_QUERY_TIMESTAMP_DISJOINT;
d3d.device->CreateQuery(&qd, &queries->disjoint);
qd.Query = D3D11_QUERY_TIMESTAMP;
d3d.device->CreateQuery(&qd, &queries->frameStart);
d3d.device->CreateQuery(&qd, &queries->frameEnd);
if(queries->disjoint != NULL &&
queries->frameStart != NULL &&
queries->frameEnd != NULL)
{
queries->valid = qtrue;
d3d.context->Begin(queries->disjoint);
d3d.context->End(queries->frameStart);
}
else
{
COM_RELEASE(queries->disjoint);
COM_RELEASE(queries->frameStart);
COM_RELEASE(queries->frameEnd);
}
}
static void EndQueries()
{
// finish this frame
FrameQueries* queries = &d3d.frameQueries[d3d.frameQueriesWriteIndex];
if(queries->valid)
{
d3d.context->End(queries->frameEnd);
d3d.context->End(queries->disjoint);
d3d.frameQueriesWriteIndex = (d3d.frameQueriesWriteIndex + 1) % ARRAY_LEN(d3d.frameQueries);
}
// try to grab a previous frame's results
D3D10_QUERY_DATA_TIMESTAMP_DISJOINT disjoint = { 0 };
backEnd.pc3D[RB_USEC_GPU] = 0; // pessimism...
queries = &d3d.frameQueries[d3d.frameQueriesReadIndex];
if(queries->valid &&
d3d.context->GetData(queries->disjoint, &disjoint, sizeof(disjoint), D3D11_ASYNC_GETDATA_DONOTFLUSH) == S_OK)
{
UINT64 start = 0;
UINT64 end = 0;
if(!disjoint.Disjoint &&
disjoint.Frequency > 0 &&
d3d.context->GetData(queries->frameStart, &start, sizeof(UINT64), D3D11_ASYNC_GETDATA_DONOTFLUSH) == S_OK &&
d3d.context->GetData(queries->frameEnd, &end, sizeof(UINT64), D3D11_ASYNC_GETDATA_DONOTFLUSH) == S_OK)
{
backEnd.pc3D[RB_USEC_GPU] = int(((end - start) * UINT64(1000000)) / disjoint.Frequency);
}
d3d.frameQueriesReadIndex = (d3d.frameQueriesReadIndex + 1) % ARRAY_LEN(d3d.frameQueries);
}
}
static void GAL_BeginFrame()
{
BeginQueries();
d3d.frameSeed[0] = (float)rand() / (float)RAND_MAX;
d3d.frameSeed[1] = (float)rand() / (float)RAND_MAX;
const FLOAT clearColor[4] = { 0.0f, 0.0f, 0.0f, 1.0f };
const FLOAT clearColorDebug[4] = { 1.0f, 0.0f, 0.5f, 1.0f };
d3d.context->ClearRenderTargetView(d3d.renderTargetViewMS, r_clear->integer ? clearColorDebug : clearColor);
d3d.context->ClearDepthStencilView(d3d.depthStencilView, D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.0f, 0);
ApplyPipeline(PID_GENERIC);
ApplyViewportAndScissor(0, 0, glConfig.vidWidth, glConfig.vidHeight, glConfig.vidHeight);
}
static void GAL_EndFrame()
{
float scaleX = 1.0f;
float scaleY = 1.0f;
if(r_fullscreen->integer == 1 && r_mode->integer == VIDEOMODE_UPSCALE)
{
if(r_blitMode->integer == BLITMODE_CENTERED)
{
scaleX = (float)glConfig.vidWidth / (float)glInfo.winWidth;
scaleY = (float)glConfig.vidHeight / (float)glInfo.winHeight;
}
else if(r_blitMode->integer == BLITMODE_ASPECT)
{
const float ars = (float)glConfig.vidWidth / (float)glConfig.vidHeight;
const float ard = (float)glInfo.winWidth / (float)glInfo.winHeight;
if(ard > ars)
{
scaleX = ars / ard;
scaleY = 1.0f;
}
else
{
scaleX = 1.0f;
scaleY = ard / ars;
}
}
if(scaleX != 1.0f || scaleY != 1.0f)
{
const FLOAT clearColor[4] = { 0.0f, 0.0f, 0.0f, 1.0f };
d3d.context->ClearRenderTargetView(d3d.backBufferRTView, clearColor);
}
}
d3d.context->ResolveSubresource(d3d.resolveTexture, 0, d3d.renderTargetTextureMS, 0, DXGI_FORMAT_R8G8B8A8_UNORM);
d3d.postPSData.gamma = 1.0f / r_gamma->value;
d3d.postPSData.brightness = r_brightness->value;
d3d.postPSData.greyscale = r_greyscale->value;
d3d.postVSData.scaleX = scaleX;
d3d.postVSData.scaleY = scaleY;
ApplyPipeline(PID_POST_PROCESS);
ApplyState(GLS_DEPTHTEST_DISABLE, CT_TWO_SIDED, qfalse);
UploadPendingShaderData();
BindImage(0, tr.whiteImage);
d3d.context->PSSetShaderResources(0, 1, &d3d.resolveTextureShaderView);
ApplySamplerState(0, TW_CLAMP_TO_EDGE, qtrue);
ApplyViewportAndScissor(0, 0, glInfo.winWidth, glInfo.winHeight, glInfo.winHeight);
d3d.context->Draw(3, 0);
EndQueries();
// Present interval flags
// flags: DXGI_PRESENT_DO_NOT_SEQUENCE DXGI_PRESENT_ALLOW_TEARING
const HRESULT hr = d3d.swapChain->Present(abs(r_swapInterval->integer), 0);
if(hr == DXGI_ERROR_DEVICE_REMOVED)
{
ri.Error(ERR_FATAL, "Direct3D device was removed! Reason: %s", GetDeviceRemovedReason());
}
else if(hr == DXGI_ERROR_DEVICE_RESET)
{
ri.Printf(PRINT_ERROR, "Direct3D device was reset! Restarting the video system...");
Cmd_ExecuteString("vid_restart;");
}
}
static void GAL_BeginSkyAndClouds()
{
const float clipPlane[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
memcpy(d3d.oldSkyClipPlane, d3d.clipPlane, sizeof(d3d.oldSkyClipPlane));
memcpy(d3d.clipPlane, clipPlane, sizeof(d3d.clipPlane));
ApplyState(GLS_SRCBLEND_ONE | GLS_DSTBLEND_ZERO, CT_TWO_SIDED, qfalse);
d3d.texEnv = TE_DISABLED;
UploadPendingShaderData();
UINT numVP = 1;
d3d.context->RSGetViewports(&numVP, &d3d.oldSkyViewport);
d3d.oldSkyViewport.MinDepth = 1.0f;
d3d.oldSkyViewport.MaxDepth = 1.0f;
d3d.context->RSSetViewports(1, &d3d.oldSkyViewport);
}
static void GAL_EndSkyAndClouds()
{
d3d.oldSkyViewport.MinDepth = 0.0f;
d3d.oldSkyViewport.MaxDepth = 1.0f;
d3d.context->RSSetViewports(1, &d3d.oldSkyViewport);
memcpy(d3d.clipPlane, d3d.oldSkyClipPlane, sizeof(d3d.clipPlane));
}
static void WriteInvalidImage(int w, int h, int alignment, colorSpace_t colorSpace, void* out)
{
if(colorSpace == CS_RGBA)
memset(out, 0x7F, PAD(w * 4, alignment) * h);
else if(colorSpace == CS_BGR)
memset(out, 0x7F, PAD(w * 3, alignment) * h);
}
static void GAL_ReadPixels(int x, int y, int w, int h, int alignment, colorSpace_t colorSpace, void* out)
{
if(d3d.readbackTexture == NULL)
{
WriteInvalidImage(w, h, alignment, colorSpace, out);
return;
}
d3d.context->CopyResource(d3d.readbackTexture, d3d.backBufferTexture);
D3D11_MAPPED_SUBRESOURCE ms;
HRESULT hr = d3d.context->Map(d3d.readbackTexture, 0, D3D11_MAP_READ, NULL, &ms);
if(FAILED(hr))
{
WriteInvalidImage(w, h, alignment, colorSpace, out);
return;
}
if(colorSpace == CS_RGBA)
{
const byte* srcRow = (const byte*)ms.pData;
byte* dstRow = (byte*)out + PAD(w * 4, alignment) * (h - 1);
for(int y = 0; y < h; ++y)
{
const byte* s = srcRow;
byte* d = dstRow;
for(int x = 0; x < w; ++x)
{
d[0] = s[0];
d[1] = s[1];
d[2] = s[2];
d[3] = 255;
d += 4;
s += 4;
}
srcRow += ms.RowPitch;
dstRow -= PAD(w * 4, alignment);
}
}
else if(colorSpace == CS_BGR)
{
const byte* srcRow = (const byte*)ms.pData;
byte* dstRow = (byte*)out + PAD(w * 3, alignment) * (h - 1);
for(int y = 0; y < h; ++y)
{
const byte* s = srcRow;
byte* d = dstRow;
for(int x = 0; x < w; ++x)
{
d[2] = s[0];
d[1] = s[1];
d[0] = s[2];
d += 3;
s += 4;
}
srcRow += ms.RowPitch;
dstRow -= PAD(w * 3, alignment);
}
}
d3d.context->Unmap(d3d.readbackTexture, NULL);
}
static void GAL_CreateTexture(image_t* image, int mipCount, int w, int h)
{
if(d3d.textureCount >= ARRAY_LEN(d3d.textures))
ri.Error(ERR_FATAL, "Too many textures allocated for the Direct3D 11 back-end");
CreateTexture(&d3d.textures[d3d.textureCount], image, mipCount, w, h);
image->texnum = d3d.textureCount++;
}
static void GAL_UpdateTexture(image_t* image, int mip, int x, int y, int w, int h, const void* data)
{
ID3D11Texture2D* texture = d3d.textures[image->texnum].texture;
if(texture == NULL)
{
return;
}
const int rowBytes = image->format == TF_RGBA8 ? (w * 4) : w;
const int imageBytes = rowBytes * h;
D3D11_BOX box;
box.front = 0;
box.back = 1;
box.left = x;
box.right = x + w;
box.top = y;
box.bottom = y + h;
d3d.context->UpdateSubresource(texture, mip, &box, data, rowBytes, imageBytes);
}
static void GAL_UpdateScratch(image_t* image, int w, int h, const void* data, qbool dirty)
{
if(image->texnum <= 0 || image->texnum > ARRAY_LEN(d3d.textures))
{
return;
}
if(w != image->width || h != image->height)
{
image->width = w;
image->height = h;
CreateTexture(&d3d.textures[image->texnum], image, 1, w, h);
GAL_UpdateTexture(image, 0, 0, 0, w, h, data);
}
else if(dirty)
{
GAL_UpdateTexture(image, 0, 0, 0, w, h, data);
}
}
static void GAL_CreateTextureEx(image_t* image, int mipCount, int mipOffset, int w, int h, const void* mip0)
{
enum { GroupSize = 8, GroupMask = GroupSize - 1 };
// needed so we don't bind a resource that's already bound
ID3D11ShaderResourceView* const srvNull = NULL;
ID3D11UnorderedAccessView* const uavNull = NULL;
ID3D11Buffer* const bufferNull = NULL;
GAL_CreateTexture(image, mipCount - mipOffset, image->width, image->height);
const Texture* const texture = &d3d.textures[image->texnum];
// upload source mip 0
const int rowBytes = w * 4;
const int imageBytes = rowBytes * h;
D3D11_BOX box;
box.front = 0;
box.back = 1;
box.left = 0;
box.right = w;
box.top = 0;
box.bottom = h;
d3d.context->UpdateSubresource(d3d.mipGenTextures[2].texture, 0, &box, mip0, rowBytes, imageBytes);
GammaToLinearCSData dataG2L;
dataG2L.gamma = r_mipGenGamma->value;
// create a linear color space copy of source mip 0
int readIndex = 2;
int writeIndex = 0;
ResetShaderData(d3d.mipGammaToLinearConstBuffer, &dataG2L, sizeof(dataG2L));
d3d.context->CSSetShader(d3d.mipGammaToLinearComputeShader, NULL, 0);
d3d.context->CSSetConstantBuffers(0, 1, &bufferNull);
d3d.context->CSSetShaderResources(0, 1, &srvNull);
d3d.context->CSSetUnorderedAccessViews(0, 1, &uavNull, NULL);
d3d.context->CSSetConstantBuffers(0, 1, &d3d.mipGammaToLinearConstBuffer);
d3d.context->CSSetShaderResources(0, 1, &d3d.mipGenTextures[readIndex].srv);
d3d.context->CSSetUnorderedAccessViews(0, 1, &d3d.mipGenTextures[writeIndex].uav, NULL);
d3d.context->Dispatch((w + GroupMask) / GroupSize, (h + GroupMask) / GroupSize, 1);
LinearToGammaCSData dataL2G;
dataL2G.intensity = r_intensity->value;
dataL2G.invGamma = 1.0f / r_mipGenGamma->value;
dataL2G.blendColor[3] = 0.0f;
// copy to destination mip 0 now if needed
if(mipOffset == 0)
{
readIndex = 0;
writeIndex = 2;
ResetShaderData(d3d.mipLinearToGammaConstBuffer, &dataL2G, sizeof(dataL2G));
d3d.context->CSSetShader(d3d.mipLinearToGammaComputeShader, NULL, 0);
d3d.context->CSSetConstantBuffers(0, 1, &bufferNull);
d3d.context->CSSetShaderResources(0, 1, &srvNull);
d3d.context->CSSetUnorderedAccessViews(0, 1, &uavNull, NULL);
d3d.context->CSSetConstantBuffers(0, 1, &d3d.mipLinearToGammaConstBuffer);
d3d.context->CSSetShaderResources(0, 1, &d3d.mipGenTextures[readIndex].srv);
d3d.context->CSSetUnorderedAccessViews(0, 1, &d3d.mipGenTextures[writeIndex].uav, NULL);
d3d.context->Dispatch((w + GroupMask) / GroupSize, (h + GroupMask) / GroupSize, 1);
box.front = 0;
box.back = 1;
box.left = 0;
box.right = w;
box.top = 0;
box.bottom = h;
d3d.context->CopySubresourceRegion(texture->texture, 0, 0, 0, 0, d3d.mipGenTextures[2].texture, 0, &box);
}
Down4CSData dataDown;
memcpy(dataDown.weights, tr.mipFilter, sizeof(dataDown.weights));
dataDown.clampMode = image->wrapClampMode == TW_REPEAT ? 0 : 1;
for(int i = 1; i < mipCount; ++i)
{
const int w1 = w;
const int h1 = h;
w = max(w / 2, 1);
h = max(h / 2, 1);
// down-sample on the X-axis
readIndex = 0;
writeIndex = 1;
dataDown.scale[0] = w1 / w;
dataDown.scale[1] = 1;
dataDown.maxSize[0] = w1 - 1;
dataDown.maxSize[1] = h1 - 1;
dataDown.offset[0] = 1;
dataDown.offset[1] = 0;
ResetShaderData(d3d.mipDownSampleConstBuffer, &dataDown, sizeof(dataDown));
d3d.context->CSSetShader(d3d.mipDownSampleComputeShader, NULL, 0);
d3d.context->CSSetConstantBuffers(0, 1, &bufferNull);
d3d.context->CSSetShaderResources(0, 1, &srvNull);
d3d.context->CSSetUnorderedAccessViews(0, 1, &uavNull, NULL);
d3d.context->CSSetConstantBuffers(0, 1, &d3d.mipDownSampleConstBuffer);
d3d.context->CSSetShaderResources(0, 1, &d3d.mipGenTextures[readIndex].srv);
d3d.context->CSSetUnorderedAccessViews(0, 1, &d3d.mipGenTextures[writeIndex].uav, NULL);
d3d.context->Dispatch((w + GroupMask) / GroupSize, (h1 + GroupMask) / GroupSize, 1);
// down-sample on the Y-axis
readIndex = 1;
writeIndex = 0;
dataDown.scale[0] = 1;
dataDown.scale[1] = h1 / h;
dataDown.maxSize[0] = w - 1;
dataDown.maxSize[1] = h1 - 1;
dataDown.offset[0] = 0;
dataDown.offset[1] = 1;
ResetShaderData(d3d.mipDownSampleConstBuffer, &dataDown, sizeof(dataDown));
d3d.context->CSSetShaderResources(0, 1, &srvNull);
d3d.context->CSSetUnorderedAccessViews(0, 1, &uavNull, NULL);
d3d.context->CSSetShaderResources(0, 1, &d3d.mipGenTextures[readIndex].srv);
d3d.context->CSSetUnorderedAccessViews(0, 1, &d3d.mipGenTextures[writeIndex].uav, NULL);
d3d.context->Dispatch((w + GroupMask) / GroupSize, (h + GroupMask) / GroupSize, 1);
const int destMip = i - mipOffset;
if(destMip >= 0)
{
// convert to final format
readIndex = 0;
writeIndex = 2;
memcpy(dataL2G.blendColor, r_mipBlendColors[r_colorMipLevels->integer ? destMip : 0], sizeof(dataL2G.blendColor));
ResetShaderData(d3d.mipLinearToGammaConstBuffer, &dataL2G, sizeof(dataL2G));
d3d.context->CSSetShader(d3d.mipLinearToGammaComputeShader, NULL, 0);
d3d.context->CSSetConstantBuffers(0, 1, &bufferNull);
d3d.context->CSSetShaderResources(0, 1, &srvNull);
d3d.context->CSSetUnorderedAccessViews(0, 1, &uavNull, NULL);
d3d.context->CSSetConstantBuffers(0, 1, &d3d.mipLinearToGammaConstBuffer);
d3d.context->CSSetShaderResources(0, 1, &d3d.mipGenTextures[readIndex].srv);
d3d.context->CSSetUnorderedAccessViews(0, 1, &d3d.mipGenTextures[writeIndex].uav, NULL);
d3d.context->Dispatch((w + GroupMask) / GroupSize, (h + GroupMask) / GroupSize, 1);
// write out the result
box.front = 0;
box.back = 1;
box.left = 0;
box.right = w;
box.top = 0;
box.bottom = h;
d3d.context->CopySubresourceRegion(texture->texture, destMip, 0, 0, 0, d3d.mipGenTextures[2].texture, 0, &box);
}
}
}
static void DrawGeneric()
{
AppendVertexData(&d3d.indexBuffer, tess.indexes, tess.numIndexes);
if(d3d.splitBufferOffsets)
{
AppendVertexData(&d3d.vertexBuffers[VB_POSITION], tess.xyz, tess.numVertexes);
}
for(int i = 0; i < tess.shader->numStages; ++i)
{
const shaderStage_t* stage = tess.xstages[i];
if(d3d.splitBufferOffsets)
{
AppendVertexData(&d3d.vertexBuffers[VB_TEXCOORD], tess.svars[i].texcoordsptr, tess.numVertexes);
AppendVertexData(&d3d.vertexBuffers[VB_COLOR], tess.svars[i].colors, tess.numVertexes);
if(stage->mtStages == 1)
{
AppendVertexData(&d3d.vertexBuffers[VB_TEXCOORD2], tess.svars[i + 1].texcoordsptr, tess.numVertexes);
}
}
else
{
const void* pointers[VB_COUNT];
pointers[VB_POSITION] = tess.xyz;
pointers[VB_NORMAL] = NULL;
pointers[VB_TEXCOORD] = tess.svars[i].texcoordsptr;
pointers[VB_TEXCOORD2] = stage->mtStages == 1 ? tess.svars[i + 1].texcoordsptr : NULL;
pointers[VB_COLOR] = tess.svars[i].colors;
AppendVertexDataGroup(pointers, tess.numVertexes);
}
ApplyState(stage->stateBits, tess.shader->cullType, tess.shader->polygonOffset);
BindBundle(0, &stage->bundle);
if(stage->mtStages == 1)
{
const shaderStage_t* stage2 = tess.xstages[i + 1];
d3d.texEnv = stage2->mtEnv;
BindBundle(1, &stage2->bundle);
i += 1;
}
else
{
BindImage(1, tr.whiteImage);
d3d.texEnv = TE_DISABLED;
}
UploadPendingShaderData();
DrawIndexed(tess.numIndexes);
}
if(tess.drawFog)
{
if(d3d.splitBufferOffsets)
{
AppendVertexData(&d3d.vertexBuffers[VB_TEXCOORD], tess.svarsFog.texcoordsptr, tess.numVertexes);
AppendVertexData(&d3d.vertexBuffers[VB_COLOR], tess.svarsFog.colors, tess.numVertexes);
}
else
{
const void* pointers[VB_COUNT];
pointers[VB_POSITION] = tess.xyz;
pointers[VB_NORMAL] = NULL;
pointers[VB_TEXCOORD] = tess.svarsFog.texcoordsptr;
pointers[VB_TEXCOORD2] = NULL;
pointers[VB_COLOR] = tess.svarsFog.colors;
AppendVertexDataGroup(pointers, tess.numVertexes);
}
ApplyState(tess.fogStateBits, tess.shader->cullType, tess.shader->polygonOffset);
BindImage(0, tr.fogImage);
BindImage(1, tr.whiteImage);
d3d.texEnv = TE_DISABLED;
UploadPendingShaderData();
DrawIndexed(tess.numIndexes);
}
}
static void DrawDynamicLight()
{
const int stageIndex = tess.shader->lightingStages[ST_DIFFUSE];
const shaderStage_t* stage = tess.xstages[stageIndex];
AppendVertexData(&d3d.indexBuffer, tess.dlIndexes, tess.dlNumIndexes);
if(d3d.splitBufferOffsets)
{
AppendVertexData(&d3d.vertexBuffers[VB_POSITION], tess.xyz, tess.numVertexes);
AppendVertexData(&d3d.vertexBuffers[VB_NORMAL], tess.normal, tess.numVertexes);
AppendVertexData(&d3d.vertexBuffers[VB_TEXCOORD], tess.svars[stageIndex].texcoordsptr, tess.numVertexes);
AppendVertexData(&d3d.vertexBuffers[VB_COLOR], tess.svars[stageIndex].colors, tess.numVertexes);
}
else
{
const void* pointers[VB_COUNT];
pointers[VB_POSITION] = tess.xyz;
pointers[VB_NORMAL] = tess.normal;
pointers[VB_TEXCOORD] = tess.svars[stageIndex].texcoordsptr;
pointers[VB_TEXCOORD2] = NULL;
pointers[VB_COLOR] = tess.svars[stageIndex].colors;
AppendVertexDataGroup(pointers, tess.numVertexes);
}
const int oldAlphaTestBits = stage->stateBits & GLS_ATEST_BITS;
const int newBits = GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE | GLS_DEPTHFUNC_EQUAL;
ApplyState(oldAlphaTestBits | newBits, tess.shader->cullType, tess.shader->polygonOffset);
BindBundle(0, &stage->bundle);
UploadPendingShaderData();
DrawIndexed(tess.dlNumIndexes);
}
static void DrawSoftSprite()
{
AppendVertexData(&d3d.indexBuffer, tess.indexes, tess.numIndexes);
if(d3d.splitBufferOffsets)
{
AppendVertexData(&d3d.vertexBuffers[VB_POSITION], tess.xyz, tess.numVertexes);
}
for(int i = 0; i < tess.shader->numStages; ++i)
{
const shaderStage_t* stage = tess.xstages[i];
if(d3d.splitBufferOffsets)
{
AppendVertexData(&d3d.vertexBuffers[VB_TEXCOORD], tess.svars[i].texcoordsptr, tess.numVertexes);
AppendVertexData(&d3d.vertexBuffers[VB_COLOR], tess.svars[i].colors, tess.numVertexes);
}
else
{
const void* pointers[VB_COUNT];
pointers[VB_POSITION] = tess.xyz;
pointers[VB_NORMAL] = NULL;
pointers[VB_TEXCOORD] = tess.svars[i].texcoordsptr;
pointers[VB_TEXCOORD2] = NULL;
pointers[VB_COLOR] = tess.svars[i].colors;
AppendVertexDataGroup(pointers, tess.numVertexes);
}
ApplyState(stage->stateBits, tess.shader->cullType, tess.shader->polygonOffset);
BindBundle(0, &stage->bundle);
UploadPendingShaderData();
DrawIndexed(tess.numIndexes);
}
}
static void GAL_Draw(drawType_t type)
{
if(type == DT_GENERIC)
{
ApplyPipeline(PID_GENERIC);
DrawGeneric();
}
else if(type == DT_DYNAMIC_LIGHT)
{
ApplyPipeline(PID_DYNAMIC_LIGHT);
DrawDynamicLight();
}
else if(type == DT_SOFT_SPRITE)
{
ApplyPipeline(PID_SOFT_SPRITE);
DrawSoftSprite();
}
}
static void GAL_Begin2D()
{
R_MakeIdentityMatrix(d3d.modelViewMatrix);
R_MakeOrthoProjectionMatrix(d3d.projectionMatrix, glConfig.vidWidth, glConfig.vidHeight);
ApplyViewportAndScissor(0, 0, glConfig.vidWidth, glConfig.vidHeight, glConfig.vidHeight);
ApplyState(GLS_DEFAULT_2D, CT_TWO_SIDED, qfalse);
}
static void GAL_Begin3D()
{
ApplyPipeline(PID_GENERIC);
memcpy(d3d.projectionMatrix, backEnd.viewParms.projectionMatrix, sizeof(d3d.projectionMatrix));
ApplyViewportAndScissor(backEnd.viewParms.viewportX, backEnd.viewParms.viewportY, backEnd.viewParms.viewportWidth, backEnd.viewParms.viewportHeight, glConfig.vidHeight);
if(backEnd.refdef.rdflags & RDF_HYPERSPACE)
{
const FLOAT c = 0.25 + 0.5 * sin(M_PI * (backEnd.refdef.time & 0x01FF) / 0x0200);
const FLOAT clearColor[4] = { c, c, c, 1.0f };
d3d.context->ClearRenderTargetView(d3d.backBufferRTView, clearColor);
return;
}
d3d.context->ClearDepthStencilView(d3d.depthStencilView, D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.0f, 0);
if(r_fastsky->integer && !(backEnd.refdef.rdflags & RDF_NOWORLDMODEL))
{
const FLOAT clearColor[4] = { 0.0f, 0.0f, 0.0f, 1.0f };
d3d.context->ClearRenderTargetView(d3d.backBufferRTView, clearColor);
}
if(backEnd.viewParms.isPortal)
{
float plane[4];
plane[0] = backEnd.viewParms.portalPlane.normal[0];
plane[1] = backEnd.viewParms.portalPlane.normal[1];
plane[2] = backEnd.viewParms.portalPlane.normal[2];
plane[3] = backEnd.viewParms.portalPlane.dist;
float plane2[4];
plane2[0] = DotProduct(backEnd.viewParms.orient.axis[0], plane);
plane2[1] = DotProduct(backEnd.viewParms.orient.axis[1], plane);
plane2[2] = DotProduct(backEnd.viewParms.orient.axis[2], plane);
plane2[3] = DotProduct(plane, backEnd.viewParms.orient.origin) - plane[3];
float* o = plane;
const float* m = s_flipMatrix;
const float* v = plane2;
o[0] = m[0] * v[0] + m[4] * v[1] + m[ 8] * v[2] + m[12] * v[3];
o[1] = m[1] * v[0] + m[5] * v[1] + m[ 9] * v[2] + m[13] * v[3];
o[2] = m[2] * v[0] + m[6] * v[1] + m[10] * v[2] + m[14] * v[3];
o[3] = m[3] * v[0] + m[7] * v[1] + m[11] * v[2] + m[15] * v[3];
memcpy(d3d.clipPlane, plane, sizeof(d3d.clipPlane));
}
else
{
const float clipPlane[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
memcpy(d3d.clipPlane, clipPlane, sizeof(d3d.clipPlane));
}
ApplyState(GLS_DEFAULT, CT_TWO_SIDED, qfalse);
}
static void GAL_SetModelViewMatrix(const float* matrix)
{
memcpy(d3d.modelViewMatrix, matrix, sizeof(d3d.modelViewMatrix));
}
static void GAL_SetDepthRange(double near, double far)
{
D3D11_VIEWPORT viewport;
UINT numVP = 1;
d3d.context->RSGetViewports(&numVP, &viewport);
viewport.MinDepth = (float)near;
viewport.MaxDepth = (float)far;
d3d.context->RSSetViewports(1, &viewport);
}
static void GAL_BeginDynamicLight()
{
const dlight_t* const dl = tess.light;
d3d.osEyePos[0] = backEnd.orient.viewOrigin[0];
d3d.osEyePos[1] = backEnd.orient.viewOrigin[1];
d3d.osEyePos[2] = backEnd.orient.viewOrigin[2];
d3d.osEyePos[3] = 1.0f;
d3d.osLightPos[0] = dl->transformed[0];
d3d.osLightPos[1] = dl->transformed[1];
d3d.osLightPos[2] = dl->transformed[2];
d3d.osLightPos[3] = 1.0f;
d3d.lightColor[0] = dl->color[0];
d3d.lightColor[1] = dl->color[1];
d3d.lightColor[2] = dl->color[2];
d3d.lightRadius = 1.0f / Square(dl->radius);
}
static void GAL_PrintInfo()
{
ri.Printf(PRINT_ALL, "Direct3D device feature level: %s\n", d3d.device->GetFeatureLevel() == D3D_FEATURE_LEVEL_11_0 ? "11.0" : "10.1");
ri.Printf(PRINT_ALL, "Direct3D vertex buffer upload strategy: %s\n", d3d.splitBufferOffsets ? "split offsets" : "sync'd offsets");
if(d3d.adapterInfo.valid)
{
ri.Printf(PRINT_ALL, "%6d MB of dedicated GPU memory\n", d3d.adapterInfo.dedicatedVideoMemoryMB);
ri.Printf(PRINT_ALL, "%6d MB of shared system memory\n", d3d.adapterInfo.sharedSystemMemoryMB);
ri.Printf(PRINT_ALL, "%6d MB of dedicated system memory\n", d3d.adapterInfo.dedicatedSystemMemoryMB);
}
}
qbool GAL_GetD3D11(graphicsAPILayer_t* rb)
{
rb->Init = &GAL_Init;
rb->ShutDown = &GAL_ShutDown;
rb->BeginSkyAndClouds = &GAL_BeginSkyAndClouds;
rb->EndSkyAndClouds = &GAL_EndSkyAndClouds;
rb->ReadPixels = &GAL_ReadPixels;
rb->BeginFrame = &GAL_BeginFrame;
rb->EndFrame = &GAL_EndFrame;
rb->CreateTexture = &GAL_CreateTexture;
rb->UpdateTexture = &GAL_UpdateTexture;
rb->UpdateScratch = &GAL_UpdateScratch;
rb->CreateTextureEx = &GAL_CreateTextureEx;
rb->Draw = &GAL_Draw;
rb->Begin2D = &GAL_Begin2D;
rb->Begin3D = &GAL_Begin3D;
rb->SetModelViewMatrix = &GAL_SetModelViewMatrix;
rb->SetDepthRange = &GAL_SetDepthRange;
rb->BeginDynamicLight = &GAL_BeginDynamicLight;
rb->PrintInfo = &GAL_PrintInfo;
return qtrue;
}
#else
#include "tr_local.h"
qbool GAL_GetD3D11(graphicsAPILayer_t* rb)
{
return qfalse;
}
#endif