mirror of
https://github.com/hrydgard/ppsspp.git
synced 2024-12-12 07:56:03 +00:00
1889 lines
63 KiB
C++
1889 lines
63 KiB
C++
#include "ppsspp_config.h"
|
|
|
|
#include "Common/GPU/thin3d.h"
|
|
#if PPSSPP_PLATFORM(UWP)
|
|
#define ptr_D3DCompile D3DCompile
|
|
#else
|
|
#include "Common/GPU/D3D11/D3D11Loader.h"
|
|
#endif
|
|
#include "Common/System/Display.h"
|
|
|
|
#include "Common/Data/Convert/ColorConv.h"
|
|
#include "Common/Data/Convert/SmallDataConvert.h"
|
|
#include "Common/Data/Encoding/Utf8.h"
|
|
#include "Common/TimeUtil.h"
|
|
#include "Common/Log.h"
|
|
|
|
#include <map>
|
|
|
|
#include <cfloat>
|
|
#include <D3Dcommon.h>
|
|
#include <d3d11.h>
|
|
#include <d3d11_1.h>
|
|
#include <D3Dcompiler.h>
|
|
|
|
#ifdef __MINGW32__
|
|
#undef __uuidof
|
|
#define __uuidof(type) IID_##type
|
|
#endif
|
|
|
|
namespace Draw {
|
|
|
|
static constexpr int MAX_BOUND_TEXTURES = 8;
|
|
|
|
// A problem is that we can't get the D3Dcompiler.dll without using a later SDK than 7.1, which was the last that
|
|
// supported XP. A possible solution might be here:
|
|
// https://tedwvc.wordpress.com/2014/01/01/how-to-target-xp-with-vc2012-or-vc2013-and-continue-to-use-the-windows-8-x-sdk/
|
|
|
|
class D3D11Pipeline;
|
|
class D3D11BlendState;
|
|
class D3D11DepthStencilState;
|
|
class D3D11SamplerState;
|
|
class D3D11Buffer;
|
|
class D3D11RasterState;
|
|
class D3D11Framebuffer;
|
|
|
|
// This must stay POD for the memcmp to work reliably.
|
|
struct D3D11DepthStencilKey {
|
|
DepthStencilStateDesc desc;
|
|
u8 writeMask;
|
|
u8 compareMask;
|
|
|
|
bool operator < (const D3D11DepthStencilKey &other) const {
|
|
return memcmp(this, &other, sizeof(D3D11DepthStencilKey)) < 0;
|
|
}
|
|
};
|
|
|
|
|
|
class D3D11DepthStencilState : public DepthStencilState {
|
|
public:
|
|
~D3D11DepthStencilState() {}
|
|
DepthStencilStateDesc desc;
|
|
};
|
|
|
|
class D3D11DrawContext : public DrawContext {
|
|
public:
|
|
D3D11DrawContext(ID3D11Device *device, ID3D11DeviceContext *deviceContext, ID3D11Device1 *device1, ID3D11DeviceContext1 *deviceContext1, IDXGISwapChain *swapChain, D3D_FEATURE_LEVEL featureLevel, HWND hWnd, std::vector<std::string> deviceList, int maxInflightFrames);
|
|
~D3D11DrawContext();
|
|
|
|
const DeviceCaps &GetDeviceCaps() const override {
|
|
return caps_;
|
|
}
|
|
std::vector<std::string> GetDeviceList() const override {
|
|
return deviceList_;
|
|
}
|
|
uint32_t GetSupportedShaderLanguages() const override {
|
|
return (uint32_t)ShaderLanguage::HLSL_D3D11;
|
|
}
|
|
uint32_t GetDataFormatSupport(DataFormat fmt) const override;
|
|
|
|
InputLayout *CreateInputLayout(const InputLayoutDesc &desc) override;
|
|
DepthStencilState *CreateDepthStencilState(const DepthStencilStateDesc &desc) override;
|
|
BlendState *CreateBlendState(const BlendStateDesc &desc) override;
|
|
SamplerState *CreateSamplerState(const SamplerStateDesc &desc) override;
|
|
RasterState *CreateRasterState(const RasterStateDesc &desc) override;
|
|
Buffer *CreateBuffer(size_t size, uint32_t usageFlags) override;
|
|
Pipeline *CreateGraphicsPipeline(const PipelineDesc &desc, const char *tag) override;
|
|
Texture *CreateTexture(const TextureDesc &desc) override;
|
|
ShaderModule *CreateShaderModule(ShaderStage stage, ShaderLanguage language, const uint8_t *data, size_t dataSize, const char *tag) override;
|
|
Framebuffer *CreateFramebuffer(const FramebufferDesc &desc) override;
|
|
|
|
void UpdateBuffer(Buffer *buffer, const uint8_t *data, size_t offset, size_t size, UpdateBufferFlags flags) override;
|
|
void UpdateTextureLevels(Texture *texture, const uint8_t **data, TextureCallback initDataCallback, int numLevels) override;
|
|
|
|
void CopyFramebufferImage(Framebuffer *src, int level, int x, int y, int z, Framebuffer *dst, int dstLevel, int dstX, int dstY, int dstZ, int width, int height, int depth, int channelBits, const char *tag) override;
|
|
bool BlitFramebuffer(Framebuffer *src, int srcX1, int srcY1, int srcX2, int srcY2, Framebuffer *dst, int dstX1, int dstY1, int dstX2, int dstY2, int channelBits, FBBlitFilter filter, const char *tag) override;
|
|
bool CopyFramebufferToMemory(Framebuffer *src, int channelBits, int x, int y, int w, int h, Draw::DataFormat format, void *pixels, int pixelStride, ReadbackMode mode, const char *tag) override;
|
|
|
|
// These functions should be self explanatory.
|
|
void BindFramebufferAsRenderTarget(Framebuffer *fbo, const RenderPassInfo &rp, const char *tag) override;
|
|
void BindFramebufferAsTexture(Framebuffer *fbo, int binding, FBChannel channelBit, int layer) override;
|
|
|
|
void GetFramebufferDimensions(Framebuffer *fbo, int *w, int *h) override;
|
|
|
|
void Invalidate(InvalidationFlags flags) override;
|
|
|
|
void BindTextures(int start, int count, Texture **textures, TextureBindFlags flags) override;
|
|
void BindNativeTexture(int index, void *nativeTexture) override;
|
|
void BindSamplerStates(int start, int count, SamplerState **states) override;
|
|
void BindVertexBuffer(Buffer *buffers, int offset) override;
|
|
void BindIndexBuffer(Buffer *indexBuffer, int offset) override;
|
|
void BindPipeline(Pipeline *pipeline) override;
|
|
|
|
void UpdateDynamicUniformBuffer(const void *ub, size_t size) override;
|
|
|
|
// Raster state
|
|
void SetScissorRect(int left, int top, int width, int height) override;
|
|
void SetViewport(const Viewport &viewport) override;
|
|
void SetBlendFactor(float color[4]) override {
|
|
if (memcmp(blendFactor_, color, sizeof(float) * 4)) {
|
|
memcpy(blendFactor_, color, sizeof(float) * 4);
|
|
blendFactorDirty_ = true;
|
|
}
|
|
}
|
|
void SetStencilParams(uint8_t refValue, uint8_t writeMask, uint8_t compareMask) override {
|
|
stencilRef_ = refValue;
|
|
stencilWriteMask_ = writeMask;
|
|
stencilCompareMask_ = compareMask;
|
|
stencilDirty_ = true;
|
|
}
|
|
|
|
|
|
void Draw(int vertexCount, int offset) override;
|
|
void DrawIndexed(int vertexCount, int offset) override;
|
|
void DrawUP(const void *vdata, int vertexCount) override;
|
|
void Clear(int mask, uint32_t colorval, float depthVal, int stencilVal) override;
|
|
|
|
void BeginFrame(DebugFlags debugFlags) override;
|
|
void EndFrame() override;
|
|
void Present(PresentMode presentMode, int vblanks) override;
|
|
|
|
int GetFrameCount() override { return frameCount_; }
|
|
|
|
std::string GetInfoString(InfoField info) const override {
|
|
switch (info) {
|
|
case InfoField::APIVERSION: return "Direct3D 11";
|
|
case InfoField::VENDORSTRING: return adapterDesc_;
|
|
case InfoField::VENDOR: return "";
|
|
case InfoField::DRIVER: return "-";
|
|
case InfoField::SHADELANGVERSION:
|
|
switch (featureLevel_) {
|
|
case D3D_FEATURE_LEVEL_9_1: return "Feature Level 9.1"; break;
|
|
case D3D_FEATURE_LEVEL_9_2: return "Feature Level 9.2"; break;
|
|
case D3D_FEATURE_LEVEL_9_3: return "Feature Level 9.3"; break;
|
|
case D3D_FEATURE_LEVEL_10_0: return "Feature Level 10.0"; break;
|
|
case D3D_FEATURE_LEVEL_10_1: return "Feature Level 10.1"; break;
|
|
case D3D_FEATURE_LEVEL_11_0: return "Feature Level 11.0"; break;
|
|
case D3D_FEATURE_LEVEL_11_1: return "Feature Level 11.1"; break;
|
|
case D3D_FEATURE_LEVEL_12_0: return "Feature Level 12.0"; break;
|
|
case D3D_FEATURE_LEVEL_12_1: return "Feature Level 12.1"; break;
|
|
}
|
|
return "Unknown feature level";
|
|
case InfoField::APINAME: return "Direct3D 11";
|
|
default: return "?";
|
|
}
|
|
}
|
|
|
|
uint64_t GetNativeObject(NativeObject obj, void *srcObject) override;
|
|
|
|
void HandleEvent(Event ev, int width, int height, void *param1, void *param2) override;
|
|
|
|
void SetInvalidationCallback(InvalidationCallback callback) override {
|
|
invalidationCallback_ = callback;
|
|
}
|
|
|
|
private:
|
|
void ApplyCurrentState();
|
|
|
|
ID3D11DepthStencilState *GetCachedDepthStencilState(const D3D11DepthStencilState *state, uint8_t stencilWriteMask, uint8_t stencilCompareMask);
|
|
|
|
HWND hWnd_;
|
|
ID3D11Device *device_;
|
|
ID3D11Device1 *device1_;
|
|
ID3D11DeviceContext *context_;
|
|
ID3D11DeviceContext1 *context1_;
|
|
IDXGISwapChain *swapChain_;
|
|
|
|
ID3D11Texture2D *bbRenderTargetTex_ = nullptr; // NOT OWNED
|
|
ID3D11RenderTargetView *bbRenderTargetView_ = nullptr;
|
|
// Strictly speaking we don't need a depth buffer for the backbuffer.
|
|
ID3D11Texture2D *bbDepthStencilTex_ = nullptr;
|
|
ID3D11DepthStencilView *bbDepthStencilView_ = nullptr;
|
|
|
|
AutoRef<Framebuffer> curRenderTarget_;
|
|
ID3D11RenderTargetView *curRenderTargetView_ = nullptr;
|
|
ID3D11DepthStencilView *curDepthStencilView_ = nullptr;
|
|
// Needed to rotate stencil/viewport rectangles properly
|
|
int bbWidth_ = 0;
|
|
int bbHeight_ = 0;
|
|
int curRTWidth_ = 0;
|
|
int curRTHeight_ = 0;
|
|
|
|
AutoRef<D3D11Pipeline> curPipeline_;
|
|
DeviceCaps caps_{};
|
|
|
|
AutoRef<D3D11BlendState> curBlend_;
|
|
AutoRef<D3D11DepthStencilState> curDepthStencil_;
|
|
AutoRef<D3D11RasterState> curRaster_;
|
|
|
|
std::map<D3D11DepthStencilKey, ID3D11DepthStencilState *> depthStencilCache_;
|
|
|
|
ID3D11InputLayout *curInputLayout_ = nullptr;
|
|
ID3D11VertexShader *curVS_ = nullptr;
|
|
ID3D11PixelShader *curPS_ = nullptr;
|
|
ID3D11GeometryShader *curGS_ = nullptr;
|
|
D3D11_PRIMITIVE_TOPOLOGY curTopology_ = D3D11_PRIMITIVE_TOPOLOGY_UNDEFINED;
|
|
|
|
ID3D11Buffer *nextVertexBuffer_ = nullptr;
|
|
UINT nextVertexBufferOffset_ = 0;
|
|
|
|
bool dirtyIndexBuffer_ = false;
|
|
ID3D11Buffer *nextIndexBuffer_ = nullptr;
|
|
UINT nextIndexBufferOffset_ = 0;
|
|
|
|
InvalidationCallback invalidationCallback_;
|
|
int frameCount_ = FRAME_TIME_HISTORY_LENGTH;
|
|
|
|
// Dynamic state
|
|
float blendFactor_[4]{};
|
|
bool blendFactorDirty_ = false;
|
|
uint8_t stencilRef_ = 0;
|
|
uint8_t stencilWriteMask_ = 0xFF;
|
|
uint8_t stencilCompareMask_ = 0xFF;
|
|
|
|
bool stencilDirty_ = true;
|
|
|
|
// Temporaries
|
|
ID3D11Texture2D *packTexture_ = nullptr;
|
|
Buffer *upBuffer_ = nullptr;
|
|
|
|
// System info
|
|
D3D_FEATURE_LEVEL featureLevel_;
|
|
std::string adapterDesc_;
|
|
std::vector<std::string> deviceList_;
|
|
};
|
|
|
|
D3D11DrawContext::D3D11DrawContext(ID3D11Device *device, ID3D11DeviceContext *deviceContext, ID3D11Device1 *device1, ID3D11DeviceContext1 *deviceContext1, IDXGISwapChain *swapChain, D3D_FEATURE_LEVEL featureLevel, HWND hWnd, std::vector<std::string> deviceList, int maxInflightFrames)
|
|
: hWnd_(hWnd),
|
|
device_(device),
|
|
context_(deviceContext1),
|
|
device1_(device1),
|
|
context1_(deviceContext1),
|
|
featureLevel_(featureLevel),
|
|
swapChain_(swapChain),
|
|
deviceList_(deviceList) {
|
|
|
|
// We no longer support Windows Phone.
|
|
_assert_(featureLevel_ >= D3D_FEATURE_LEVEL_9_3);
|
|
|
|
// Seems like a fair approximation...
|
|
caps_.dualSourceBlend = featureLevel_ >= D3D_FEATURE_LEVEL_10_0;
|
|
caps_.depthClampSupported = featureLevel_ >= D3D_FEATURE_LEVEL_10_0;
|
|
// SV_ClipDistance# seems to be 10+.
|
|
caps_.clipDistanceSupported = featureLevel_ >= D3D_FEATURE_LEVEL_10_0;
|
|
caps_.cullDistanceSupported = featureLevel_ >= D3D_FEATURE_LEVEL_10_0;
|
|
|
|
caps_.depthRangeMinusOneToOne = false;
|
|
caps_.framebufferBlitSupported = false;
|
|
caps_.framebufferCopySupported = true;
|
|
caps_.framebufferDepthBlitSupported = false;
|
|
caps_.framebufferStencilBlitSupported = false;
|
|
caps_.framebufferDepthCopySupported = true;
|
|
caps_.framebufferSeparateDepthCopySupported = false; // Though could be emulated with a draw.
|
|
caps_.preferredDepthBufferFormat = DataFormat::D24_S8;
|
|
caps_.textureDepthSupported = true;
|
|
caps_.texture3DSupported = true;
|
|
caps_.fragmentShaderInt32Supported = true;
|
|
caps_.anisoSupported = true;
|
|
caps_.textureNPOTFullySupported = true;
|
|
caps_.fragmentShaderDepthWriteSupported = true;
|
|
caps_.fragmentShaderStencilWriteSupported = false;
|
|
caps_.blendMinMaxSupported = true;
|
|
caps_.multiSampleLevelsMask = 1; // More could be supported with some work.
|
|
|
|
caps_.provokingVertexLast = false; // D3D has it first, unfortunately. (and no way to change it).
|
|
|
|
caps_.presentInstantModeChange = true;
|
|
caps_.presentMaxInterval = 4;
|
|
caps_.presentModesSupported = PresentMode::FIFO | PresentMode::IMMEDIATE;
|
|
|
|
D3D11_FEATURE_DATA_D3D11_OPTIONS options{};
|
|
HRESULT result = device_->CheckFeatureSupport(D3D11_FEATURE_D3D11_OPTIONS, &options, sizeof(options));
|
|
if (SUCCEEDED(result)) {
|
|
if (options.OutputMergerLogicOp) {
|
|
// Actually, need to check that the format supports logic ops as well.
|
|
// Which normal UNORM formats don't seem to do in D3D11. So meh. We can't enable logicOp support.
|
|
// caps_.logicOpSupported = true;
|
|
}
|
|
}
|
|
|
|
IDXGIDevice* dxgiDevice = nullptr;
|
|
IDXGIAdapter* adapter = nullptr;
|
|
HRESULT hr = device_->QueryInterface(__uuidof(IDXGIDevice), reinterpret_cast<void**>(&dxgiDevice));
|
|
if (SUCCEEDED(hr)) {
|
|
hr = dxgiDevice->GetAdapter(&adapter);
|
|
if (SUCCEEDED(hr)) {
|
|
DXGI_ADAPTER_DESC desc;
|
|
adapter->GetDesc(&desc);
|
|
adapterDesc_ = ConvertWStringToUTF8(desc.Description);
|
|
switch (desc.VendorId) {
|
|
case 0x10DE: caps_.vendor = GPUVendor::VENDOR_NVIDIA; break;
|
|
case 0x1002:
|
|
case 0x1022: caps_.vendor = GPUVendor::VENDOR_AMD; break;
|
|
case 0x163C:
|
|
case 0x8086:
|
|
case 0x8087: caps_.vendor = GPUVendor::VENDOR_INTEL; break;
|
|
// TODO: There are Windows ARM devices that could have Qualcomm here too.
|
|
// Not sure where I'll find the vendor codes for those though...
|
|
default:
|
|
caps_.vendor = GPUVendor::VENDOR_UNKNOWN;
|
|
}
|
|
caps_.deviceID = desc.DeviceId;
|
|
adapter->Release();
|
|
}
|
|
dxgiDevice->Release();
|
|
}
|
|
|
|
caps_.isTilingGPU = false;
|
|
|
|
// Hide D3D9 when we know it likely won't work well.
|
|
caps_.supportsD3D9 = true;
|
|
if (!strcmp(adapterDesc_.c_str(), "Intel(R) Iris(R) Xe Graphics")) {
|
|
caps_.supportsD3D9 = false;
|
|
}
|
|
|
|
// Temp texture for read-back of small images. Custom textures are created on demand for larger ones.
|
|
// TODO: Should really benchmark if this extra complexity has any benefit.
|
|
D3D11_TEXTURE2D_DESC packDesc{};
|
|
packDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
|
|
packDesc.BindFlags = 0;
|
|
packDesc.Width = 512;
|
|
packDesc.Height = 512;
|
|
packDesc.ArraySize = 1;
|
|
packDesc.MipLevels = 1;
|
|
packDesc.Usage = D3D11_USAGE_STAGING;
|
|
packDesc.SampleDesc.Count = 1;
|
|
packDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
|
|
hr = device_->CreateTexture2D(&packDesc, nullptr, &packTexture_);
|
|
_assert_(SUCCEEDED(hr));
|
|
|
|
shaderLanguageDesc_.Init(HLSL_D3D11);
|
|
|
|
const size_t UP_MAX_BYTES = 65536 * 24;
|
|
|
|
upBuffer_ = CreateBuffer(UP_MAX_BYTES, BufferUsageFlag::DYNAMIC | BufferUsageFlag::VERTEXDATA);
|
|
|
|
IDXGIDevice1 *dxgiDevice1 = nullptr;
|
|
hr = device_->QueryInterface(__uuidof(IDXGIDevice), reinterpret_cast<void **>(&dxgiDevice1));
|
|
if (SUCCEEDED(hr)) {
|
|
caps_.setMaxFrameLatencySupported = true;
|
|
dxgiDevice1->SetMaximumFrameLatency(maxInflightFrames);
|
|
}
|
|
}
|
|
|
|
D3D11DrawContext::~D3D11DrawContext() {
|
|
DestroyPresets();
|
|
|
|
upBuffer_->Release();
|
|
packTexture_->Release();
|
|
|
|
// Release references.
|
|
ID3D11RenderTargetView *view = nullptr;
|
|
context_->OMSetRenderTargets(1, &view, nullptr);
|
|
ID3D11ShaderResourceView *srv[2]{};
|
|
context_->PSSetShaderResources(0, 2, srv);
|
|
}
|
|
|
|
void D3D11DrawContext::HandleEvent(Event ev, int width, int height, void *param1, void *param2) {
|
|
switch (ev) {
|
|
case Event::LOST_BACKBUFFER: {
|
|
if (curRenderTargetView_ == bbRenderTargetView_ || curDepthStencilView_ == bbDepthStencilView_) {
|
|
ID3D11RenderTargetView *view = nullptr;
|
|
context_->OMSetRenderTargets(1, &view, nullptr);
|
|
curRenderTargetView_ = nullptr;
|
|
curDepthStencilView_ = nullptr;
|
|
}
|
|
bbDepthStencilView_->Release();
|
|
bbDepthStencilView_ = nullptr;
|
|
bbDepthStencilTex_->Release();
|
|
bbDepthStencilTex_ = nullptr;
|
|
curRTWidth_ = 0;
|
|
curRTHeight_ = 0;
|
|
break;
|
|
}
|
|
case Event::GOT_BACKBUFFER: {
|
|
bbRenderTargetView_ = (ID3D11RenderTargetView *)param1;
|
|
bbRenderTargetTex_ = (ID3D11Texture2D *)param2;
|
|
bbWidth_ = width;
|
|
bbHeight_ = height;
|
|
|
|
// Create matching depth stencil texture. This is not really needed for PPSSPP though,
|
|
// and probably not for most other renderers either as you're usually rendering to other render targets and
|
|
// then blitting them with a shader to the screen.
|
|
D3D11_TEXTURE2D_DESC descDepth{};
|
|
descDepth.Width = width;
|
|
descDepth.Height = height;
|
|
descDepth.MipLevels = 1;
|
|
descDepth.ArraySize = 1;
|
|
descDepth.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
|
|
descDepth.SampleDesc.Count = 1;
|
|
descDepth.SampleDesc.Quality = 0;
|
|
descDepth.Usage = D3D11_USAGE_DEFAULT;
|
|
descDepth.BindFlags = D3D11_BIND_DEPTH_STENCIL;
|
|
descDepth.CPUAccessFlags = 0;
|
|
descDepth.MiscFlags = 0;
|
|
HRESULT hr = device_->CreateTexture2D(&descDepth, nullptr, &bbDepthStencilTex_);
|
|
|
|
// Create the depth stencil view
|
|
D3D11_DEPTH_STENCIL_VIEW_DESC descDSV{};
|
|
descDSV.Format = descDepth.Format;
|
|
descDSV.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D;
|
|
descDSV.Texture2D.MipSlice = 0;
|
|
hr = device_->CreateDepthStencilView(bbDepthStencilTex_, &descDSV, &bbDepthStencilView_);
|
|
|
|
context_->OMSetRenderTargets(1, &bbRenderTargetView_, bbDepthStencilView_);
|
|
|
|
curRenderTargetView_ = bbRenderTargetView_;
|
|
curDepthStencilView_ = bbDepthStencilView_;
|
|
curRTWidth_ = width;
|
|
curRTHeight_ = height;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void D3D11DrawContext::EndFrame() {
|
|
// Fake a submit time.
|
|
frameTimeHistory_[frameCount_].firstSubmit = time_now_d();
|
|
curPipeline_ = nullptr;
|
|
}
|
|
|
|
void D3D11DrawContext::Present(PresentMode presentMode, int vblanks) {
|
|
frameTimeHistory_[frameCount_].queuePresent = time_now_d();
|
|
|
|
int interval = vblanks;
|
|
if (presentMode != PresentMode::FIFO) {
|
|
interval = 0;
|
|
}
|
|
// Safety for libretro
|
|
if (swapChain_) {
|
|
swapChain_->Present(interval, 0);
|
|
}
|
|
curRenderTargetView_ = nullptr;
|
|
curDepthStencilView_ = nullptr;
|
|
frameCount_++;
|
|
}
|
|
|
|
void D3D11DrawContext::SetViewport(const Viewport &viewport) {
|
|
DisplayRect<float> rc{ viewport.TopLeftX , viewport.TopLeftY, viewport.Width, viewport.Height };
|
|
if (curRenderTargetView_ == bbRenderTargetView_) // Only the backbuffer is actually rotated wrong!
|
|
RotateRectToDisplay(rc, curRTWidth_, curRTHeight_);
|
|
D3D11_VIEWPORT vp;
|
|
vp.TopLeftX = rc.x;
|
|
vp.TopLeftY = rc.y;
|
|
vp.Width = rc.w;
|
|
vp.Height = rc.h;
|
|
vp.MinDepth = viewport.MinDepth;
|
|
vp.MaxDepth = viewport.MaxDepth;
|
|
context_->RSSetViewports(1, &vp);
|
|
}
|
|
|
|
void D3D11DrawContext::SetScissorRect(int left, int top, int width, int height) {
|
|
_assert_(width >= 0);
|
|
_assert_(height >= 0);
|
|
DisplayRect<float> frc{ (float)left, (float)top, (float)width, (float)height };
|
|
if (curRenderTargetView_ == bbRenderTargetView_) // Only the backbuffer is actually rotated wrong!
|
|
RotateRectToDisplay(frc, curRTWidth_, curRTHeight_);
|
|
D3D11_RECT rc{};
|
|
rc.left = (INT)frc.x;
|
|
rc.top = (INT)frc.y;
|
|
rc.right = (INT)(frc.x + frc.w);
|
|
rc.bottom = (INT)(frc.y + frc.h);
|
|
context_->RSSetScissorRects(1, &rc);
|
|
}
|
|
|
|
static const D3D11_COMPARISON_FUNC compareToD3D11[] = {
|
|
D3D11_COMPARISON_NEVER,
|
|
D3D11_COMPARISON_LESS,
|
|
D3D11_COMPARISON_EQUAL,
|
|
D3D11_COMPARISON_LESS_EQUAL,
|
|
D3D11_COMPARISON_GREATER,
|
|
D3D11_COMPARISON_NOT_EQUAL,
|
|
D3D11_COMPARISON_GREATER_EQUAL,
|
|
D3D11_COMPARISON_ALWAYS
|
|
};
|
|
|
|
static const D3D11_STENCIL_OP stencilOpToD3D11[] = {
|
|
D3D11_STENCIL_OP_KEEP,
|
|
D3D11_STENCIL_OP_ZERO,
|
|
D3D11_STENCIL_OP_REPLACE,
|
|
D3D11_STENCIL_OP_INCR_SAT,
|
|
D3D11_STENCIL_OP_DECR_SAT,
|
|
D3D11_STENCIL_OP_INVERT,
|
|
D3D11_STENCIL_OP_INCR,
|
|
D3D11_STENCIL_OP_DECR,
|
|
};
|
|
|
|
static DXGI_FORMAT dataFormatToD3D11(DataFormat format) {
|
|
switch (format) {
|
|
case DataFormat::R32_FLOAT: return DXGI_FORMAT_R32_FLOAT;
|
|
case DataFormat::R32G32_FLOAT: return DXGI_FORMAT_R32G32_FLOAT;
|
|
case DataFormat::R32G32B32_FLOAT: return DXGI_FORMAT_R32G32B32_FLOAT;
|
|
case DataFormat::R32G32B32A32_FLOAT: return DXGI_FORMAT_R32G32B32A32_FLOAT;
|
|
case DataFormat::A4R4G4B4_UNORM_PACK16: return DXGI_FORMAT_B4G4R4A4_UNORM;
|
|
case DataFormat::A1R5G5B5_UNORM_PACK16: return DXGI_FORMAT_B5G5R5A1_UNORM;
|
|
case DataFormat::R5G6B5_UNORM_PACK16: return DXGI_FORMAT_B5G6R5_UNORM;
|
|
case DataFormat::R8G8B8A8_UNORM: return DXGI_FORMAT_R8G8B8A8_UNORM;
|
|
case DataFormat::R8G8B8A8_UNORM_SRGB: return DXGI_FORMAT_R8G8B8A8_UNORM_SRGB;
|
|
case DataFormat::B8G8R8A8_UNORM: return DXGI_FORMAT_B8G8R8A8_UNORM;
|
|
case DataFormat::B8G8R8A8_UNORM_SRGB: return DXGI_FORMAT_B8G8R8A8_UNORM_SRGB;
|
|
case DataFormat::R16_UNORM: return DXGI_FORMAT_R16_UNORM;
|
|
case DataFormat::R16_FLOAT: return DXGI_FORMAT_R16_FLOAT;
|
|
case DataFormat::R16G16_FLOAT: return DXGI_FORMAT_R16G16_FLOAT;
|
|
case DataFormat::R16G16B16A16_FLOAT: return DXGI_FORMAT_R16G16B16A16_FLOAT;
|
|
case DataFormat::D24_S8: return DXGI_FORMAT_D24_UNORM_S8_UINT;
|
|
case DataFormat::D16: return DXGI_FORMAT_D16_UNORM;
|
|
case DataFormat::D32F: return DXGI_FORMAT_D32_FLOAT;
|
|
case DataFormat::D32F_S8: return DXGI_FORMAT_D32_FLOAT_S8X24_UINT;
|
|
case DataFormat::BC1_RGBA_UNORM_BLOCK: return DXGI_FORMAT_BC1_UNORM;
|
|
case DataFormat::BC2_UNORM_BLOCK: return DXGI_FORMAT_BC2_UNORM;
|
|
case DataFormat::BC3_UNORM_BLOCK: return DXGI_FORMAT_BC3_UNORM;
|
|
case DataFormat::BC4_UNORM_BLOCK: return DXGI_FORMAT_BC4_UNORM;
|
|
case DataFormat::BC5_UNORM_BLOCK: return DXGI_FORMAT_BC5_UNORM;
|
|
case DataFormat::BC7_UNORM_BLOCK: return DXGI_FORMAT_BC7_UNORM;
|
|
default:
|
|
return DXGI_FORMAT_UNKNOWN;
|
|
}
|
|
}
|
|
|
|
static D3D11_PRIMITIVE_TOPOLOGY primToD3D11[] = {
|
|
D3D11_PRIMITIVE_TOPOLOGY_POINTLIST,
|
|
D3D11_PRIMITIVE_TOPOLOGY_LINELIST,
|
|
D3D11_PRIMITIVE_TOPOLOGY_LINESTRIP,
|
|
D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST,
|
|
D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP,
|
|
D3D11_PRIMITIVE_TOPOLOGY_UNDEFINED,
|
|
// Tesselation shader only
|
|
D3D11_PRIMITIVE_TOPOLOGY_1_CONTROL_POINT_PATCHLIST, // ???
|
|
// These are for geometry shaders only.
|
|
D3D11_PRIMITIVE_TOPOLOGY_LINELIST_ADJ,
|
|
D3D11_PRIMITIVE_TOPOLOGY_LINESTRIP_ADJ,
|
|
D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST_ADJ,
|
|
D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP_ADJ,
|
|
};
|
|
|
|
inline void CopyStencilSide(D3D11_DEPTH_STENCILOP_DESC &side, const StencilSetup &input) {
|
|
side.StencilFunc = compareToD3D11[(int)input.compareOp];
|
|
side.StencilDepthFailOp = stencilOpToD3D11[(int)input.depthFailOp];
|
|
side.StencilFailOp = stencilOpToD3D11[(int)input.failOp];
|
|
side.StencilPassOp = stencilOpToD3D11[(int)input.passOp];
|
|
}
|
|
|
|
static const D3D11_BLEND_OP blendOpToD3D11[] = {
|
|
D3D11_BLEND_OP_ADD,
|
|
D3D11_BLEND_OP_SUBTRACT,
|
|
D3D11_BLEND_OP_REV_SUBTRACT,
|
|
D3D11_BLEND_OP_MIN,
|
|
D3D11_BLEND_OP_MAX,
|
|
};
|
|
|
|
static const D3D11_BLEND blendToD3D11[] = {
|
|
D3D11_BLEND_ZERO,
|
|
D3D11_BLEND_ONE,
|
|
D3D11_BLEND_SRC_COLOR,
|
|
D3D11_BLEND_INV_SRC_COLOR,
|
|
D3D11_BLEND_DEST_COLOR,
|
|
D3D11_BLEND_INV_DEST_COLOR,
|
|
D3D11_BLEND_SRC_ALPHA,
|
|
D3D11_BLEND_INV_SRC_ALPHA,
|
|
D3D11_BLEND_DEST_ALPHA,
|
|
D3D11_BLEND_INV_DEST_ALPHA,
|
|
D3D11_BLEND_BLEND_FACTOR,
|
|
D3D11_BLEND_INV_BLEND_FACTOR,
|
|
D3D11_BLEND_BLEND_FACTOR,
|
|
D3D11_BLEND_INV_BLEND_FACTOR,
|
|
D3D11_BLEND_SRC1_COLOR,
|
|
D3D11_BLEND_INV_SRC1_COLOR,
|
|
D3D11_BLEND_SRC1_ALPHA,
|
|
D3D11_BLEND_INV_SRC1_ALPHA,
|
|
};
|
|
|
|
class D3D11BlendState : public BlendState {
|
|
public:
|
|
~D3D11BlendState() {
|
|
bs->Release();
|
|
}
|
|
ID3D11BlendState *bs;
|
|
float blendFactor[4];
|
|
};
|
|
|
|
ID3D11DepthStencilState *D3D11DrawContext::GetCachedDepthStencilState(const D3D11DepthStencilState *state, uint8_t stencilWriteMask, uint8_t stencilCompareMask) {
|
|
D3D11DepthStencilKey key;
|
|
key.desc = state->desc;
|
|
key.writeMask = stencilWriteMask;
|
|
key.compareMask = stencilCompareMask;
|
|
|
|
auto findResult = depthStencilCache_.find(key);
|
|
|
|
if (findResult != depthStencilCache_.end()) {
|
|
return findResult->second;
|
|
}
|
|
|
|
// OK, create and insert.
|
|
D3D11_DEPTH_STENCIL_DESC d3ddesc{};
|
|
d3ddesc.DepthEnable = state->desc.depthTestEnabled;
|
|
d3ddesc.DepthWriteMask = state->desc.depthWriteEnabled ? D3D11_DEPTH_WRITE_MASK_ALL : D3D11_DEPTH_WRITE_MASK_ZERO;
|
|
d3ddesc.DepthFunc = compareToD3D11[(int)state->desc.depthCompare];
|
|
d3ddesc.StencilEnable = state->desc.stencilEnabled;
|
|
d3ddesc.StencilReadMask = stencilCompareMask;
|
|
d3ddesc.StencilWriteMask = stencilWriteMask;
|
|
if (d3ddesc.StencilEnable) {
|
|
CopyStencilSide(d3ddesc.FrontFace, state->desc.stencil);
|
|
CopyStencilSide(d3ddesc.BackFace, state->desc.stencil);
|
|
}
|
|
|
|
ID3D11DepthStencilState *dss = nullptr;
|
|
if (SUCCEEDED(device_->CreateDepthStencilState(&d3ddesc, &dss))) {
|
|
depthStencilCache_[key] = dss;
|
|
return dss;
|
|
} else {
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
DepthStencilState *D3D11DrawContext::CreateDepthStencilState(const DepthStencilStateDesc &desc) {
|
|
D3D11DepthStencilState *dss = new D3D11DepthStencilState();
|
|
dss->desc = desc;
|
|
return dynamic_cast<DepthStencilState *>(dss);
|
|
}
|
|
|
|
BlendState *D3D11DrawContext::CreateBlendState(const BlendStateDesc &desc) {
|
|
D3D11BlendState *bs = new D3D11BlendState();
|
|
D3D11_BLEND_DESC d3ddesc{};
|
|
d3ddesc.AlphaToCoverageEnable = FALSE;
|
|
d3ddesc.IndependentBlendEnable = FALSE;
|
|
d3ddesc.RenderTarget[0].BlendEnable = desc.enabled;
|
|
d3ddesc.RenderTarget[0].RenderTargetWriteMask = desc.colorMask;
|
|
d3ddesc.RenderTarget[0].BlendOp = blendOpToD3D11[(int)desc.eqCol];
|
|
d3ddesc.RenderTarget[0].BlendOpAlpha = blendOpToD3D11[(int)desc.eqAlpha];
|
|
d3ddesc.RenderTarget[0].SrcBlend = blendToD3D11[(int)desc.srcCol];
|
|
d3ddesc.RenderTarget[0].SrcBlendAlpha = blendToD3D11[(int)desc.srcAlpha];
|
|
d3ddesc.RenderTarget[0].DestBlend = blendToD3D11[(int)desc.dstCol];
|
|
d3ddesc.RenderTarget[0].DestBlendAlpha = blendToD3D11[(int)desc.dstAlpha];
|
|
if (SUCCEEDED(device_->CreateBlendState(&d3ddesc, &bs->bs)))
|
|
return bs;
|
|
delete bs;
|
|
return nullptr;
|
|
}
|
|
|
|
class D3D11RasterState : public RasterState {
|
|
public:
|
|
~D3D11RasterState() {
|
|
if (rs)
|
|
rs->Release();
|
|
}
|
|
ID3D11RasterizerState *rs;
|
|
};
|
|
|
|
RasterState *D3D11DrawContext::CreateRasterState(const RasterStateDesc &desc) {
|
|
D3D11RasterState *rs = new D3D11RasterState();
|
|
D3D11_RASTERIZER_DESC d3ddesc{};
|
|
d3ddesc.FillMode = D3D11_FILL_SOLID;
|
|
switch (desc.cull) {
|
|
case CullMode::BACK: d3ddesc.CullMode = D3D11_CULL_BACK; break;
|
|
case CullMode::FRONT: d3ddesc.CullMode = D3D11_CULL_FRONT; break;
|
|
default:
|
|
case CullMode::NONE: d3ddesc.CullMode = D3D11_CULL_NONE; break;
|
|
}
|
|
d3ddesc.FrontCounterClockwise = desc.frontFace == Facing::CCW;
|
|
d3ddesc.ScissorEnable = true; // We always run with scissor enabled
|
|
d3ddesc.DepthClipEnable = true;
|
|
if (SUCCEEDED(device_->CreateRasterizerState(&d3ddesc, &rs->rs)))
|
|
return rs;
|
|
delete rs;
|
|
return nullptr;
|
|
}
|
|
|
|
class D3D11SamplerState : public SamplerState {
|
|
public:
|
|
~D3D11SamplerState() {
|
|
if (ss)
|
|
ss->Release();
|
|
}
|
|
ID3D11SamplerState *ss = nullptr;
|
|
};
|
|
|
|
static const D3D11_TEXTURE_ADDRESS_MODE taddrToD3D11[] = {
|
|
D3D11_TEXTURE_ADDRESS_WRAP,
|
|
D3D11_TEXTURE_ADDRESS_MIRROR,
|
|
D3D11_TEXTURE_ADDRESS_CLAMP,
|
|
D3D11_TEXTURE_ADDRESS_BORDER,
|
|
};
|
|
|
|
SamplerState *D3D11DrawContext::CreateSamplerState(const SamplerStateDesc &desc) {
|
|
D3D11SamplerState *ss = new D3D11SamplerState();
|
|
D3D11_SAMPLER_DESC d3ddesc{};
|
|
d3ddesc.AddressU = taddrToD3D11[(int)desc.wrapU];
|
|
d3ddesc.AddressV = taddrToD3D11[(int)desc.wrapV];
|
|
d3ddesc.AddressW = taddrToD3D11[(int)desc.wrapW];
|
|
// TODO: Needs improvement
|
|
d3ddesc.Filter = desc.magFilter == TextureFilter::LINEAR ? D3D11_FILTER_MIN_MAG_MIP_LINEAR : D3D11_FILTER_MIN_MAG_MIP_POINT;
|
|
d3ddesc.MaxAnisotropy = 1.0f; // (UINT)desc.maxAniso;
|
|
d3ddesc.MinLOD = -FLT_MAX;
|
|
d3ddesc.MaxLOD = FLT_MAX;
|
|
d3ddesc.ComparisonFunc = compareToD3D11[(int)desc.shadowCompareFunc];
|
|
for (int i = 0; i < 4; i++) {
|
|
d3ddesc.BorderColor[i] = 1.0f;
|
|
}
|
|
if (SUCCEEDED(device_->CreateSamplerState(&d3ddesc, &ss->ss)))
|
|
return ss;
|
|
delete ss;
|
|
return nullptr;
|
|
}
|
|
|
|
// Input layout creation is delayed to pipeline creation, as we need the vertex shader bytecode.
|
|
class D3D11InputLayout : public InputLayout {
|
|
public:
|
|
D3D11InputLayout() {}
|
|
InputLayoutDesc desc;
|
|
std::vector<D3D11_INPUT_ELEMENT_DESC> elements;
|
|
UINT stride; // type to match function parameter
|
|
};
|
|
|
|
const char *semanticToD3D11(int semantic, UINT *index) {
|
|
*index = 0;
|
|
switch (semantic) {
|
|
case SEM_POSITION: return "POSITION";
|
|
case SEM_COLOR0: *index = 0; return "COLOR";
|
|
case SEM_COLOR1: *index = 1; return "COLOR";
|
|
case SEM_TEXCOORD0: *index = 0; return "TEXCOORD";
|
|
case SEM_TEXCOORD1: *index = 1; return "TEXCOORD";
|
|
case SEM_NORMAL: return "NORMAL";
|
|
case SEM_TANGENT: return "TANGENT";
|
|
case SEM_BINORMAL: return "BINORMAL"; // really BITANGENT
|
|
default: return "UNKNOWN";
|
|
}
|
|
}
|
|
|
|
InputLayout *D3D11DrawContext::CreateInputLayout(const InputLayoutDesc &desc) {
|
|
D3D11InputLayout *inputLayout = new D3D11InputLayout();
|
|
inputLayout->desc = desc;
|
|
|
|
// Translate to D3D11 elements;
|
|
for (size_t i = 0; i < desc.attributes.size(); i++) {
|
|
D3D11_INPUT_ELEMENT_DESC el;
|
|
el.AlignedByteOffset = desc.attributes[i].offset;
|
|
el.Format = dataFormatToD3D11(desc.attributes[i].format);
|
|
el.InstanceDataStepRate = 0;
|
|
el.InputSlot = 0;
|
|
el.SemanticName = semanticToD3D11(desc.attributes[i].location, &el.SemanticIndex);
|
|
el.InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
|
|
inputLayout->elements.push_back(el);
|
|
}
|
|
inputLayout->stride = desc.stride;
|
|
return inputLayout;
|
|
}
|
|
|
|
class D3D11ShaderModule : public ShaderModule {
|
|
public:
|
|
D3D11ShaderModule(const std::string &tag) : tag_(tag) { }
|
|
~D3D11ShaderModule() {
|
|
if (vs)
|
|
vs->Release();
|
|
if (ps)
|
|
ps->Release();
|
|
if (gs)
|
|
gs->Release();
|
|
}
|
|
ShaderStage GetStage() const override { return stage; }
|
|
|
|
std::vector<uint8_t> byteCode_;
|
|
ShaderStage stage;
|
|
std::string tag_;
|
|
|
|
ID3D11VertexShader *vs = nullptr;
|
|
ID3D11PixelShader *ps = nullptr;
|
|
ID3D11GeometryShader *gs = nullptr;
|
|
};
|
|
|
|
class D3D11Pipeline : public Pipeline {
|
|
public:
|
|
~D3D11Pipeline() {
|
|
if (il)
|
|
il->Release();
|
|
if (dynamicUniforms)
|
|
dynamicUniforms->Release();
|
|
for (D3D11ShaderModule *shaderModule : shaderModules) {
|
|
shaderModule->Release();
|
|
}
|
|
}
|
|
|
|
AutoRef<D3D11InputLayout> input;
|
|
ID3D11InputLayout *il = nullptr;
|
|
AutoRef<D3D11BlendState> blend;
|
|
AutoRef<D3D11RasterState> raster;
|
|
|
|
// Combined with dynamic state to key into cached D3D11DepthStencilState, to emulate dynamic parameters.
|
|
AutoRef<D3D11DepthStencilState> depthStencil;
|
|
|
|
ID3D11VertexShader *vs = nullptr;
|
|
ID3D11PixelShader *ps = nullptr;
|
|
ID3D11GeometryShader *gs = nullptr;
|
|
D3D11_PRIMITIVE_TOPOLOGY topology = D3D11_PRIMITIVE_TOPOLOGY_UNDEFINED;
|
|
|
|
std::vector<D3D11ShaderModule *> shaderModules;
|
|
|
|
size_t dynamicUniformsSize = 0;
|
|
ID3D11Buffer *dynamicUniforms = nullptr;
|
|
};
|
|
|
|
class D3D11Texture : public Texture {
|
|
public:
|
|
D3D11Texture(const TextureDesc &desc) {
|
|
width_ = desc.width;
|
|
height_ = desc.height;
|
|
depth_ = desc.depth;
|
|
format_ = desc.format;
|
|
mipLevels_ = desc.mipLevels;
|
|
}
|
|
~D3D11Texture() {
|
|
if (tex_)
|
|
tex_->Release();
|
|
if (stagingTex_)
|
|
stagingTex_->Release();
|
|
if (view_)
|
|
view_->Release();
|
|
}
|
|
|
|
bool Create(ID3D11DeviceContext *context, ID3D11Device *device, const TextureDesc &desc, bool generateMips);
|
|
|
|
bool CreateStagingTexture(ID3D11Device *device);
|
|
void UpdateTextureLevels(ID3D11DeviceContext *context, ID3D11Device *device, Texture *texture, const uint8_t *const *data, TextureCallback initDataCallback, int numLevels);
|
|
|
|
ID3D11ShaderResourceView *View() { return view_; }
|
|
|
|
private:
|
|
bool FillLevel(ID3D11DeviceContext *context, int level, int w, int h, int d, const uint8_t *const *data, TextureCallback initDataCallback);
|
|
|
|
ID3D11Texture2D *tex_ = nullptr;
|
|
ID3D11Texture2D *stagingTex_ = nullptr;
|
|
ID3D11ShaderResourceView *view_ = nullptr;
|
|
int mipLevels_ = 0;
|
|
};
|
|
|
|
bool D3D11Texture::FillLevel(ID3D11DeviceContext *context, int level, int w, int h, int d, const uint8_t *const *data, TextureCallback initDataCallback) {
|
|
D3D11_MAPPED_SUBRESOURCE mapped;
|
|
HRESULT hr = context->Map(stagingTex_, level, D3D11_MAP_WRITE, 0, &mapped);
|
|
if (!SUCCEEDED(hr)) {
|
|
tex_->Release();
|
|
tex_ = nullptr;
|
|
return false;
|
|
}
|
|
|
|
if (!initDataCallback((uint8_t *)mapped.pData, data[level], w, h, d, mapped.RowPitch, mapped.DepthPitch)) {
|
|
for (int s = 0; s < d; ++s) {
|
|
for (int y = 0; y < h; ++y) {
|
|
void *dest = (uint8_t *)mapped.pData + mapped.DepthPitch * s + mapped.RowPitch * y;
|
|
uint32_t byteStride = w * (uint32_t)DataFormatSizeInBytes(format_);
|
|
const void *src = data[level] + byteStride * (y + h * s);
|
|
memcpy(dest, src, byteStride);
|
|
}
|
|
}
|
|
}
|
|
context->Unmap(stagingTex_, level);
|
|
return true;
|
|
}
|
|
|
|
bool D3D11Texture::CreateStagingTexture(ID3D11Device *device) {
|
|
if (stagingTex_)
|
|
return true;
|
|
D3D11_TEXTURE2D_DESC descColor{};
|
|
descColor.Width = width_;
|
|
descColor.Height = height_;
|
|
descColor.MipLevels = mipLevels_;
|
|
descColor.ArraySize = 1;
|
|
descColor.Format = dataFormatToD3D11(format_);
|
|
descColor.SampleDesc.Count = 1;
|
|
descColor.SampleDesc.Quality = 0;
|
|
descColor.Usage = D3D11_USAGE_STAGING;
|
|
descColor.BindFlags = 0;
|
|
descColor.MiscFlags = 0;
|
|
descColor.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
|
|
|
|
HRESULT hr = device->CreateTexture2D(&descColor, nullptr, &stagingTex_);
|
|
if (!SUCCEEDED(hr)) {
|
|
stagingTex_->Release();
|
|
stagingTex_ = nullptr;
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool D3D11Texture::Create(ID3D11DeviceContext *context, ID3D11Device *device, const TextureDesc &desc, bool generateMips) {
|
|
D3D11_TEXTURE2D_DESC descColor{};
|
|
descColor.Width = desc.width;
|
|
descColor.Height = desc.height;
|
|
descColor.MipLevels = desc.mipLevels;
|
|
descColor.ArraySize = 1;
|
|
descColor.Format = dataFormatToD3D11(desc.format);
|
|
descColor.SampleDesc.Count = 1;
|
|
descColor.SampleDesc.Quality = 0;
|
|
descColor.Usage = D3D11_USAGE_DEFAULT;
|
|
descColor.BindFlags = generateMips ? (D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET) : D3D11_BIND_SHADER_RESOURCE;
|
|
descColor.MiscFlags = generateMips ? D3D11_RESOURCE_MISC_GENERATE_MIPS : 0;
|
|
descColor.CPUAccessFlags = 0;
|
|
|
|
// Make sure we have a staging texture if we'll need it.
|
|
if (desc.initDataCallback && !CreateStagingTexture(device)) {
|
|
return false;
|
|
}
|
|
|
|
D3D11_SUBRESOURCE_DATA *initDataParam = nullptr;
|
|
D3D11_SUBRESOURCE_DATA initData[12]{};
|
|
std::vector<uint8_t> initDataBuffer[12];
|
|
if (desc.initData.size() && !generateMips && !desc.initDataCallback) {
|
|
int w = desc.width;
|
|
int h = desc.height;
|
|
int d = desc.depth;
|
|
for (int i = 0; i < (int)desc.initData.size(); i++) {
|
|
uint32_t byteStride = w * (uint32_t)DataFormatSizeInBytes(desc.format);
|
|
initData[i].pSysMem = desc.initData[i];
|
|
initData[i].SysMemPitch = (UINT)byteStride;
|
|
initData[i].SysMemSlicePitch = (UINT)(h * byteStride);
|
|
w = (w + 1) / 2;
|
|
h = (h + 1) / 2;
|
|
d = (d + 1) / 2;
|
|
}
|
|
initDataParam = initData;
|
|
}
|
|
|
|
HRESULT hr = device->CreateTexture2D(&descColor, initDataParam, &tex_);
|
|
if (!SUCCEEDED(hr)) {
|
|
tex_ = nullptr;
|
|
return false;
|
|
}
|
|
hr = device->CreateShaderResourceView(tex_, nullptr, &view_);
|
|
if (!SUCCEEDED(hr)) {
|
|
return false;
|
|
}
|
|
|
|
if (generateMips && desc.initData.size() >= 1) {
|
|
if (desc.initDataCallback) {
|
|
if (!FillLevel(context, 0, desc.width, desc.height, desc.depth, desc.initData.data(), desc.initDataCallback)) {
|
|
tex_->Release();
|
|
return false;
|
|
}
|
|
|
|
context->CopyResource(tex_, stagingTex_);
|
|
stagingTex_->Release();
|
|
stagingTex_ = nullptr;
|
|
} else {
|
|
uint32_t byteStride = desc.width * (uint32_t)DataFormatSizeInBytes(desc.format);
|
|
context->UpdateSubresource(tex_, 0, nullptr, desc.initData[0], byteStride, 0);
|
|
}
|
|
context->GenerateMips(view_);
|
|
} else if (desc.initDataCallback) {
|
|
int w = desc.width;
|
|
int h = desc.height;
|
|
int d = desc.depth;
|
|
for (int i = 0; i < (int)desc.initData.size(); i++) {
|
|
if (!FillLevel(context, i, w, h, d, desc.initData.data(), desc.initDataCallback)) {
|
|
if (i == 0) {
|
|
return false;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
w = (w + 1) / 2;
|
|
h = (h + 1) / 2;
|
|
d = (d + 1) / 2;
|
|
}
|
|
|
|
context->CopyResource(tex_, stagingTex_);
|
|
stagingTex_->Release();
|
|
stagingTex_ = nullptr;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void D3D11Texture::UpdateTextureLevels(ID3D11DeviceContext *context, ID3D11Device *device, Texture *texture, const uint8_t * const*data, TextureCallback initDataCallback, int numLevels) {
|
|
if (!CreateStagingTexture(device)) {
|
|
return;
|
|
}
|
|
|
|
int w = width_;
|
|
int h = height_;
|
|
int d = depth_;
|
|
for (int i = 0; i < (int)numLevels; i++) {
|
|
if (!FillLevel(context, i, w, h, d, data, initDataCallback)) {
|
|
break;
|
|
}
|
|
|
|
w = (w + 1) / 2;
|
|
h = (h + 1) / 2;
|
|
d = (d + 1) / 2;
|
|
}
|
|
|
|
context->CopyResource(tex_, stagingTex_);
|
|
stagingTex_->Release();
|
|
stagingTex_ = nullptr;
|
|
}
|
|
|
|
Texture *D3D11DrawContext::CreateTexture(const TextureDesc &desc) {
|
|
if (!(GetDataFormatSupport(desc.format) & FMT_TEXTURE)) {
|
|
// D3D11 does not support this format as a texture format.
|
|
return nullptr;
|
|
}
|
|
|
|
D3D11Texture *tex = new D3D11Texture(desc);
|
|
bool generateMips = desc.generateMips;
|
|
if (desc.generateMips && !(GetDataFormatSupport(desc.format) & FMT_AUTOGEN_MIPS)) {
|
|
// D3D11 does not support autogenerating mipmaps for this format.
|
|
generateMips = false;
|
|
}
|
|
if (!tex->Create(context_, device_, desc, generateMips)) {
|
|
tex->Release();
|
|
return nullptr;
|
|
}
|
|
|
|
return tex;
|
|
}
|
|
|
|
|
|
void D3D11DrawContext::UpdateTextureLevels(Texture *texture, const uint8_t **data, TextureCallback initDataCallback, int numLevels) {
|
|
D3D11Texture *tex = (D3D11Texture *)texture;
|
|
tex->UpdateTextureLevels(context_, device_, texture, data, initDataCallback, numLevels);
|
|
}
|
|
|
|
ShaderModule *D3D11DrawContext::CreateShaderModule(ShaderStage stage, ShaderLanguage language, const uint8_t *data, size_t dataSize, const char *tag) {
|
|
if (language != ShaderLanguage::HLSL_D3D11) {
|
|
ERROR_LOG(Log::G3D, "Unsupported shader language");
|
|
return nullptr;
|
|
}
|
|
|
|
const char *vertexModel = "vs_4_0";
|
|
const char *fragmentModel = "ps_4_0";
|
|
const char *geometryModel = "gs_4_0";
|
|
if (featureLevel_ <= D3D_FEATURE_LEVEL_9_3) {
|
|
vertexModel = "vs_4_0_level_9_1";
|
|
fragmentModel = "ps_4_0_level_9_1";
|
|
geometryModel = nullptr;
|
|
}
|
|
|
|
std::string compiled;
|
|
std::string errors;
|
|
const char *target = nullptr;
|
|
switch (stage) {
|
|
case ShaderStage::Fragment: target = fragmentModel; break;
|
|
case ShaderStage::Vertex: target = vertexModel; break;
|
|
case ShaderStage::Geometry:
|
|
if (!geometryModel)
|
|
return nullptr;
|
|
target = geometryModel;
|
|
break;
|
|
case ShaderStage::Compute:
|
|
default:
|
|
Crash();
|
|
break;
|
|
}
|
|
if (!target) {
|
|
return nullptr;
|
|
}
|
|
|
|
ID3DBlob *compiledCode = nullptr;
|
|
ID3DBlob *errorMsgs = nullptr;
|
|
int flags = D3DCOMPILE_ENABLE_BACKWARDS_COMPATIBILITY;
|
|
HRESULT result = ptr_D3DCompile(data, dataSize, nullptr, nullptr, nullptr, "main", target, flags, 0, &compiledCode, &errorMsgs);
|
|
if (compiledCode) {
|
|
compiled = std::string((const char *)compiledCode->GetBufferPointer(), compiledCode->GetBufferSize());
|
|
compiledCode->Release();
|
|
}
|
|
if (errorMsgs) {
|
|
errors = std::string((const char *)errorMsgs->GetBufferPointer(), errorMsgs->GetBufferSize());
|
|
ERROR_LOG(Log::G3D, "Failed compiling %s:\n%s\n%s", tag, data, errors.c_str());
|
|
errorMsgs->Release();
|
|
}
|
|
|
|
if (result != S_OK) {
|
|
return nullptr;
|
|
}
|
|
|
|
// OK, we can now proceed
|
|
data = (const uint8_t *)compiled.c_str();
|
|
dataSize = compiled.size();
|
|
D3D11ShaderModule *module = new D3D11ShaderModule(tag);
|
|
module->stage = stage;
|
|
module->byteCode_ = std::vector<uint8_t>(data, data + dataSize);
|
|
switch (stage) {
|
|
case ShaderStage::Vertex:
|
|
result = device_->CreateVertexShader(data, dataSize, nullptr, &module->vs);
|
|
break;
|
|
case ShaderStage::Fragment:
|
|
result = device_->CreatePixelShader(data, dataSize, nullptr, &module->ps);
|
|
break;
|
|
case ShaderStage::Geometry:
|
|
result = device_->CreateGeometryShader(data, dataSize, nullptr, &module->gs);
|
|
break;
|
|
default:
|
|
ERROR_LOG(Log::G3D, "Unsupported shader stage");
|
|
result = S_FALSE;
|
|
break;
|
|
}
|
|
if (result == S_OK) {
|
|
return module;
|
|
} else {
|
|
delete module;
|
|
return nullptr;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
Pipeline *D3D11DrawContext::CreateGraphicsPipeline(const PipelineDesc &desc, const char *tag) {
|
|
D3D11Pipeline *dPipeline = new D3D11Pipeline();
|
|
dPipeline->blend = (D3D11BlendState *)desc.blend;
|
|
dPipeline->depthStencil = (D3D11DepthStencilState *)desc.depthStencil;
|
|
dPipeline->input = (D3D11InputLayout *)desc.inputLayout;
|
|
dPipeline->raster = (D3D11RasterState *)desc.raster;
|
|
dPipeline->topology = primToD3D11[(int)desc.prim];
|
|
if (desc.uniformDesc) {
|
|
dPipeline->dynamicUniformsSize = desc.uniformDesc->uniformBufferSize;
|
|
D3D11_BUFFER_DESC bufdesc{};
|
|
bufdesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
|
|
// We just round up to 16 here. If we get some garbage, that's fine.
|
|
bufdesc.ByteWidth = ((UINT)dPipeline->dynamicUniformsSize + 15) & ~15;
|
|
bufdesc.StructureByteStride = bufdesc.ByteWidth;
|
|
bufdesc.Usage = D3D11_USAGE_DYNAMIC;
|
|
bufdesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
|
|
HRESULT hr = device_->CreateBuffer(&bufdesc, nullptr, &dPipeline->dynamicUniforms);
|
|
if (FAILED(hr)) {
|
|
dPipeline->Release();
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
std::vector<D3D11ShaderModule *> shaders;
|
|
D3D11ShaderModule *vshader = nullptr;
|
|
for (auto iter : desc.shaders) {
|
|
iter->AddRef();
|
|
|
|
D3D11ShaderModule *module = (D3D11ShaderModule *)iter;
|
|
shaders.push_back(module);
|
|
switch (module->GetStage()) {
|
|
case ShaderStage::Vertex:
|
|
vshader = module;
|
|
dPipeline->vs = module->vs;
|
|
break;
|
|
case ShaderStage::Fragment:
|
|
dPipeline->ps = module->ps;
|
|
break;
|
|
case ShaderStage::Geometry:
|
|
dPipeline->gs = module->gs;
|
|
break;
|
|
}
|
|
}
|
|
dPipeline->shaderModules = shaders;
|
|
|
|
if (!vshader) {
|
|
// No vertex shader - no graphics
|
|
dPipeline->Release();
|
|
return nullptr;
|
|
}
|
|
|
|
// Can finally create the input layout
|
|
if (dPipeline->input != nullptr) {
|
|
const std::vector<D3D11_INPUT_ELEMENT_DESC> &elements = dPipeline->input->elements;
|
|
HRESULT hr = device_->CreateInputLayout(elements.data(), (UINT)elements.size(), vshader->byteCode_.data(), vshader->byteCode_.size(), &dPipeline->il);
|
|
if (!SUCCEEDED(hr)) {
|
|
Crash();
|
|
}
|
|
} else {
|
|
dPipeline->il = nullptr;
|
|
}
|
|
return dPipeline;
|
|
}
|
|
|
|
void D3D11DrawContext::UpdateDynamicUniformBuffer(const void *ub, size_t size) {
|
|
if (curPipeline_->dynamicUniformsSize != size) {
|
|
Crash();
|
|
}
|
|
D3D11_MAPPED_SUBRESOURCE map{};
|
|
context_->Map(curPipeline_->dynamicUniforms, 0, D3D11_MAP_WRITE_DISCARD, 0, &map);
|
|
memcpy(map.pData, ub, size);
|
|
context_->Unmap(curPipeline_->dynamicUniforms, 0);
|
|
}
|
|
|
|
void D3D11DrawContext::Invalidate(InvalidationFlags flags) {
|
|
if (flags & InvalidationFlags::CACHED_RENDER_STATE) {
|
|
// This is a signal to forget all our state caching.
|
|
curBlend_ = nullptr;
|
|
curDepthStencil_ = nullptr;
|
|
curRaster_ = nullptr;
|
|
curPS_ = nullptr;
|
|
curVS_ = nullptr;
|
|
curGS_ = nullptr;
|
|
curInputLayout_ = nullptr;
|
|
curTopology_ = D3D11_PRIMITIVE_TOPOLOGY_UNDEFINED;
|
|
curPipeline_ = nullptr;
|
|
}
|
|
}
|
|
|
|
void D3D11DrawContext::BindPipeline(Pipeline *pipeline) {
|
|
D3D11Pipeline *dPipeline = (D3D11Pipeline *)pipeline;
|
|
if (curPipeline_ == dPipeline)
|
|
return;
|
|
curPipeline_ = dPipeline;
|
|
}
|
|
|
|
void D3D11DrawContext::ApplyCurrentState() {
|
|
if (curBlend_ != curPipeline_->blend || blendFactorDirty_) {
|
|
context_->OMSetBlendState(curPipeline_->blend->bs, blendFactor_, 0xFFFFFFFF);
|
|
curBlend_ = curPipeline_->blend;
|
|
blendFactorDirty_ = false;
|
|
}
|
|
if (curDepthStencil_ != curPipeline_->depthStencil || stencilDirty_) {
|
|
ID3D11DepthStencilState *dss = GetCachedDepthStencilState(curPipeline_->depthStencil, stencilWriteMask_, stencilCompareMask_);
|
|
context_->OMSetDepthStencilState(dss, stencilRef_);
|
|
curDepthStencil_ = curPipeline_->depthStencil;
|
|
stencilDirty_ = false;
|
|
}
|
|
if (curRaster_ != curPipeline_->raster) {
|
|
context_->RSSetState(curPipeline_->raster->rs);
|
|
curRaster_ = curPipeline_->raster;
|
|
}
|
|
if (curInputLayout_ != curPipeline_->il) {
|
|
context_->IASetInputLayout(curPipeline_->il);
|
|
curInputLayout_ = curPipeline_->il;
|
|
}
|
|
if (curVS_ != curPipeline_->vs) {
|
|
context_->VSSetShader(curPipeline_->vs, nullptr, 0);
|
|
curVS_ = curPipeline_->vs;
|
|
}
|
|
if (curPS_ != curPipeline_->ps) {
|
|
context_->PSSetShader(curPipeline_->ps, nullptr, 0);
|
|
curPS_ = curPipeline_->ps;
|
|
}
|
|
if (curGS_ != curPipeline_->gs) {
|
|
context_->GSSetShader(curPipeline_->gs, nullptr, 0);
|
|
curGS_ = curPipeline_->gs;
|
|
}
|
|
if (curTopology_ != curPipeline_->topology) {
|
|
context_->IASetPrimitiveTopology(curPipeline_->topology);
|
|
curTopology_ = curPipeline_->topology;
|
|
}
|
|
|
|
if (curPipeline_->input != nullptr) {
|
|
context_->IASetVertexBuffers(0, 1, &nextVertexBuffer_, &curPipeline_->input->stride, &nextVertexBufferOffset_);
|
|
}
|
|
if (dirtyIndexBuffer_) {
|
|
context_->IASetIndexBuffer(nextIndexBuffer_, DXGI_FORMAT_R16_UINT, nextIndexBufferOffset_);
|
|
dirtyIndexBuffer_ = false;
|
|
}
|
|
if (curPipeline_->dynamicUniforms) {
|
|
context_->VSSetConstantBuffers(0, 1, &curPipeline_->dynamicUniforms);
|
|
context_->PSSetConstantBuffers(0, 1, &curPipeline_->dynamicUniforms);
|
|
}
|
|
}
|
|
|
|
class D3D11Buffer : public Buffer {
|
|
public:
|
|
~D3D11Buffer() {
|
|
if (buf)
|
|
buf->Release();
|
|
if (srView)
|
|
srView->Release();
|
|
}
|
|
ID3D11Buffer *buf = nullptr;
|
|
ID3D11ShaderResourceView *srView = nullptr;
|
|
size_t size;
|
|
};
|
|
|
|
Buffer *D3D11DrawContext::CreateBuffer(size_t size, uint32_t usageFlags) {
|
|
D3D11Buffer *b = new D3D11Buffer();
|
|
D3D11_BUFFER_DESC desc{};
|
|
desc.ByteWidth = (UINT)size;
|
|
desc.BindFlags = 0;
|
|
if (usageFlags & VERTEXDATA)
|
|
desc.BindFlags |= D3D11_BIND_VERTEX_BUFFER;
|
|
if (usageFlags & INDEXDATA)
|
|
desc.BindFlags |= D3D11_BIND_INDEX_BUFFER;
|
|
if (usageFlags & UNIFORM)
|
|
desc.BindFlags |= D3D11_BIND_CONSTANT_BUFFER;
|
|
|
|
desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
|
|
desc.Usage = D3D11_USAGE_DYNAMIC;
|
|
|
|
b->size = size;
|
|
HRESULT hr = device_->CreateBuffer(&desc, nullptr, &b->buf);
|
|
if (FAILED(hr)) {
|
|
delete b;
|
|
return nullptr;
|
|
}
|
|
return b;
|
|
}
|
|
|
|
void D3D11DrawContext::UpdateBuffer(Buffer *buffer, const uint8_t *data, size_t offset, size_t size, UpdateBufferFlags flags) {
|
|
D3D11Buffer *buf = (D3D11Buffer *)buffer;
|
|
if ((flags & UPDATE_DISCARD) || (offset == 0 && size == buf->size)) {
|
|
// Can just discard the old contents. This is only allowed for DYNAMIC buffers.
|
|
D3D11_MAPPED_SUBRESOURCE map;
|
|
context_->Map(buf->buf, 0, D3D11_MAP_WRITE_DISCARD, 0, &map);
|
|
memcpy(map.pData, data, size);
|
|
context_->Unmap(buf->buf, 0);
|
|
return;
|
|
}
|
|
|
|
// Should probably avoid this case.
|
|
D3D11_BOX box{};
|
|
box.left = (UINT)offset;
|
|
box.right = (UINT)(offset + size);
|
|
box.bottom = 1;
|
|
box.back = 1;
|
|
context_->UpdateSubresource(buf->buf, 0, &box, data, 0, 0);
|
|
}
|
|
|
|
void D3D11DrawContext::BindVertexBuffer(Buffer *buffer, int offset) {
|
|
// Lazy application
|
|
D3D11Buffer *buf = (D3D11Buffer *)buffer;
|
|
nextVertexBuffer_ = buf->buf;
|
|
nextVertexBufferOffset_ = offset;
|
|
}
|
|
|
|
void D3D11DrawContext::BindIndexBuffer(Buffer *indexBuffer, int offset) {
|
|
D3D11Buffer *buf = (D3D11Buffer *)indexBuffer;
|
|
// Lazy application
|
|
dirtyIndexBuffer_ = true;
|
|
nextIndexBuffer_ = buf ? buf->buf : 0;
|
|
nextIndexBufferOffset_ = buf ? offset : 0;
|
|
}
|
|
|
|
void D3D11DrawContext::Draw(int vertexCount, int offset) {
|
|
ApplyCurrentState();
|
|
context_->Draw(vertexCount, offset);
|
|
}
|
|
|
|
void D3D11DrawContext::DrawIndexed(int indexCount, int offset) {
|
|
ApplyCurrentState();
|
|
context_->DrawIndexed(indexCount, offset, 0);
|
|
}
|
|
|
|
void D3D11DrawContext::DrawUP(const void *vdata, int vertexCount) {
|
|
ApplyCurrentState();
|
|
|
|
int byteSize = vertexCount * curPipeline_->input->stride;
|
|
|
|
UpdateBuffer(upBuffer_, (const uint8_t *)vdata, 0, byteSize, Draw::UPDATE_DISCARD);
|
|
BindVertexBuffer(upBuffer_, 0);
|
|
int offset = 0;
|
|
Draw(vertexCount, offset);
|
|
}
|
|
|
|
uint32_t D3D11DrawContext::GetDataFormatSupport(DataFormat fmt) const {
|
|
DXGI_FORMAT giFmt = dataFormatToD3D11(fmt);
|
|
if (giFmt == DXGI_FORMAT_UNKNOWN)
|
|
return 0;
|
|
UINT giSupport = 0;
|
|
HRESULT result = device_->CheckFormatSupport(giFmt, &giSupport);
|
|
if (FAILED(result))
|
|
return 0;
|
|
uint32_t support = 0;
|
|
if (giSupport & D3D11_FORMAT_SUPPORT_TEXTURE2D)
|
|
support |= FMT_TEXTURE;
|
|
if (giSupport & D3D11_FORMAT_SUPPORT_RENDER_TARGET)
|
|
support |= FMT_RENDERTARGET;
|
|
if (giSupport & D3D11_FORMAT_SUPPORT_IA_VERTEX_BUFFER)
|
|
support |= FMT_INPUTLAYOUT;
|
|
if (giSupport & D3D11_FORMAT_SUPPORT_DEPTH_STENCIL)
|
|
support |= FMT_DEPTHSTENCIL;
|
|
if (giSupport & D3D11_FORMAT_SUPPORT_MIP_AUTOGEN)
|
|
support |= FMT_AUTOGEN_MIPS;
|
|
return support;
|
|
}
|
|
|
|
// A D3D11Framebuffer is a D3D11Framebuffer plus all the textures it owns.
|
|
class D3D11Framebuffer : public Framebuffer {
|
|
public:
|
|
D3D11Framebuffer(int width, int height) {
|
|
width_ = width;
|
|
height_ = height;
|
|
}
|
|
~D3D11Framebuffer() {
|
|
if (colorTex)
|
|
colorTex->Release();
|
|
if (colorRTView)
|
|
colorRTView->Release();
|
|
if (colorSRView)
|
|
colorSRView->Release();
|
|
if (depthSRView)
|
|
depthSRView->Release();
|
|
if (depthStencilTex)
|
|
depthStencilTex->Release();
|
|
if (depthStencilRTView)
|
|
depthStencilRTView->Release();
|
|
}
|
|
|
|
ID3D11Texture2D *colorTex = nullptr;
|
|
ID3D11RenderTargetView *colorRTView = nullptr;
|
|
ID3D11ShaderResourceView *colorSRView = nullptr;
|
|
ID3D11ShaderResourceView *depthSRView = nullptr;
|
|
DXGI_FORMAT colorFormat = DXGI_FORMAT_UNKNOWN;
|
|
|
|
ID3D11Texture2D *depthStencilTex = nullptr;
|
|
ID3D11DepthStencilView *depthStencilRTView = nullptr;
|
|
DXGI_FORMAT depthStencilFormat = DXGI_FORMAT_UNKNOWN;
|
|
};
|
|
|
|
Framebuffer *D3D11DrawContext::CreateFramebuffer(const FramebufferDesc &desc) {
|
|
HRESULT hr;
|
|
D3D11Framebuffer *fb = new D3D11Framebuffer(desc.width, desc.height);
|
|
|
|
// We don't (yet?) support multiview for D3D11. Not sure if there's a way to do it.
|
|
// Texture arrays are supported but we don't have any other use cases yet.
|
|
_dbg_assert_(desc.numLayers == 1);
|
|
|
|
fb->colorFormat = DXGI_FORMAT_R8G8B8A8_UNORM;
|
|
D3D11_TEXTURE2D_DESC descColor{};
|
|
descColor.Width = desc.width;
|
|
descColor.Height = desc.height;
|
|
descColor.MipLevels = 1;
|
|
descColor.ArraySize = 1;
|
|
descColor.Format = fb->colorFormat;
|
|
descColor.SampleDesc.Count = 1;
|
|
descColor.SampleDesc.Quality = 0;
|
|
descColor.Usage = D3D11_USAGE_DEFAULT;
|
|
descColor.BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE;
|
|
descColor.CPUAccessFlags = 0;
|
|
descColor.MiscFlags = 0;
|
|
hr = device_->CreateTexture2D(&descColor, nullptr, &fb->colorTex);
|
|
if (FAILED(hr)) {
|
|
delete fb;
|
|
return nullptr;
|
|
}
|
|
hr = device_->CreateRenderTargetView(fb->colorTex, nullptr, &fb->colorRTView);
|
|
if (FAILED(hr)) {
|
|
delete fb;
|
|
return nullptr;
|
|
}
|
|
hr = device_->CreateShaderResourceView(fb->colorTex, nullptr, &fb->colorSRView);
|
|
if (FAILED(hr)) {
|
|
delete fb;
|
|
return nullptr;
|
|
}
|
|
|
|
if (desc.z_stencil) {
|
|
fb->depthStencilFormat = DXGI_FORMAT_D24_UNORM_S8_UINT;
|
|
D3D11_TEXTURE2D_DESC descDepth{};
|
|
descDepth.Width = desc.width;
|
|
descDepth.Height = desc.height;
|
|
descDepth.MipLevels = 1;
|
|
descDepth.ArraySize = 1;
|
|
descDepth.Format = DXGI_FORMAT_R24G8_TYPELESS; // so we can create an R24X8 view of it.
|
|
descDepth.SampleDesc.Count = 1;
|
|
descDepth.SampleDesc.Quality = 0;
|
|
descDepth.Usage = D3D11_USAGE_DEFAULT;
|
|
descDepth.BindFlags = D3D11_BIND_DEPTH_STENCIL | D3D11_BIND_SHADER_RESOURCE;
|
|
descDepth.CPUAccessFlags = 0;
|
|
descDepth.MiscFlags = 0;
|
|
hr = device_->CreateTexture2D(&descDepth, nullptr, &fb->depthStencilTex);
|
|
if (FAILED(hr)) {
|
|
delete fb;
|
|
return nullptr;
|
|
}
|
|
D3D11_DEPTH_STENCIL_VIEW_DESC descDSV{};
|
|
descDSV.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
|
|
descDSV.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D;
|
|
descDSV.Texture2D.MipSlice = 0;
|
|
hr = device_->CreateDepthStencilView(fb->depthStencilTex, &descDSV, &fb->depthStencilRTView);
|
|
if (FAILED(hr)) {
|
|
delete fb;
|
|
return nullptr;
|
|
}
|
|
|
|
D3D11_SHADER_RESOURCE_VIEW_DESC depthViewDesc{};
|
|
depthViewDesc.Format = DXGI_FORMAT_R24_UNORM_X8_TYPELESS;
|
|
depthViewDesc.Texture2D.MostDetailedMip = 0;
|
|
depthViewDesc.Texture2D.MipLevels = 1;
|
|
depthViewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
|
|
hr = device_->CreateShaderResourceView(fb->depthStencilTex, &depthViewDesc, &fb->depthSRView);
|
|
if (FAILED(hr)) {
|
|
WARN_LOG(Log::G3D, "Failed to create SRV for depth buffer.");
|
|
fb->depthSRView = nullptr;
|
|
}
|
|
}
|
|
|
|
return fb;
|
|
}
|
|
|
|
void D3D11DrawContext::BindTextures(int start, int count, Texture **textures, TextureBindFlags flags) {
|
|
// Collect the resource views from the textures.
|
|
ID3D11ShaderResourceView *views[MAX_BOUND_TEXTURES];
|
|
_assert_(start + count <= ARRAY_SIZE(views));
|
|
for (int i = 0; i < count; i++) {
|
|
D3D11Texture *tex = (D3D11Texture *)textures[i];
|
|
views[i] = tex ? tex->View() : nullptr;
|
|
}
|
|
context_->PSSetShaderResources(start, count, views);
|
|
}
|
|
|
|
void D3D11DrawContext::BindNativeTexture(int index, void *nativeTexture) {
|
|
// Collect the resource views from the textures.
|
|
ID3D11ShaderResourceView *view = (ID3D11ShaderResourceView *)nativeTexture;
|
|
context_->PSSetShaderResources(index, 1, &view);
|
|
}
|
|
|
|
void D3D11DrawContext::BindSamplerStates(int start, int count, SamplerState **states) {
|
|
ID3D11SamplerState *samplers[MAX_BOUND_TEXTURES];
|
|
_assert_(start + count <= ARRAY_SIZE(samplers));
|
|
for (int i = 0; i < count; i++) {
|
|
D3D11SamplerState *samp = (D3D11SamplerState *)states[i];
|
|
samplers[i] = samp ? samp->ss : nullptr;
|
|
}
|
|
context_->PSSetSamplers(start, count, samplers);
|
|
}
|
|
|
|
void D3D11DrawContext::Clear(int mask, uint32_t colorval, float depthVal, int stencilVal) {
|
|
if ((mask & FBChannel::FB_COLOR_BIT) && curRenderTargetView_) {
|
|
float colorRGBA[4];
|
|
Uint8x4ToFloat4(colorRGBA, colorval);
|
|
context_->ClearRenderTargetView(curRenderTargetView_, colorRGBA);
|
|
}
|
|
if ((mask & (FBChannel::FB_DEPTH_BIT | FBChannel::FB_STENCIL_BIT)) && curDepthStencilView_) {
|
|
UINT clearFlag = 0;
|
|
if (mask & FBChannel::FB_DEPTH_BIT)
|
|
clearFlag |= D3D11_CLEAR_DEPTH;
|
|
if (mask & FBChannel::FB_STENCIL_BIT)
|
|
clearFlag |= D3D11_CLEAR_STENCIL;
|
|
context_->ClearDepthStencilView(curDepthStencilView_, clearFlag, depthVal, stencilVal);
|
|
}
|
|
}
|
|
|
|
void D3D11DrawContext::BeginFrame(DebugFlags debugFlags) {
|
|
FrameTimeData &frameTimeData = frameTimeHistory_.Add(frameCount_);
|
|
frameTimeData.afterFenceWait = time_now_d();
|
|
frameTimeData.frameBegin = frameTimeData.afterFenceWait;
|
|
|
|
context_->OMSetRenderTargets(1, &curRenderTargetView_, curDepthStencilView_);
|
|
|
|
if (curBlend_ != nullptr) {
|
|
context_->OMSetBlendState(curBlend_->bs, blendFactor_, 0xFFFFFFFF);
|
|
}
|
|
if (curDepthStencil_ != nullptr) {
|
|
context_->OMSetDepthStencilState(GetCachedDepthStencilState(curDepthStencil_, stencilWriteMask_, stencilCompareMask_), stencilRef_);
|
|
}
|
|
if (curRaster_ != nullptr) {
|
|
context_->RSSetState(curRaster_->rs);
|
|
}
|
|
context_->IASetInputLayout(curInputLayout_);
|
|
context_->VSSetShader(curVS_, nullptr, 0);
|
|
context_->PSSetShader(curPS_, nullptr, 0);
|
|
context_->GSSetShader(curGS_, nullptr, 0);
|
|
if (curTopology_ != D3D11_PRIMITIVE_TOPOLOGY_UNDEFINED) {
|
|
context_->IASetPrimitiveTopology(curTopology_);
|
|
}
|
|
if (curPipeline_ != nullptr) {
|
|
context_->IASetVertexBuffers(0, 1, &nextVertexBuffer_, &curPipeline_->input->stride, &nextVertexBufferOffset_);
|
|
context_->IASetIndexBuffer(nextIndexBuffer_, DXGI_FORMAT_R16_UINT, nextIndexBufferOffset_);
|
|
if (curPipeline_->dynamicUniforms) {
|
|
context_->VSSetConstantBuffers(0, 1, &curPipeline_->dynamicUniforms);
|
|
context_->PSSetConstantBuffers(0, 1, &curPipeline_->dynamicUniforms);
|
|
}
|
|
}
|
|
}
|
|
|
|
void D3D11DrawContext::CopyFramebufferImage(Framebuffer *srcfb, int level, int x, int y, int z, Framebuffer *dstfb, int dstLevel, int dstX, int dstY, int dstZ, int width, int height, int depth, int channelBit, const char *tag) {
|
|
D3D11Framebuffer *src = (D3D11Framebuffer *)srcfb;
|
|
D3D11Framebuffer *dst = (D3D11Framebuffer *)dstfb;
|
|
|
|
ID3D11Texture2D *srcTex = nullptr;
|
|
ID3D11Texture2D *dstTex = nullptr;
|
|
switch (channelBit) {
|
|
case FBChannel::FB_COLOR_BIT:
|
|
srcTex = src->colorTex;
|
|
dstTex = dst->colorTex;
|
|
break;
|
|
case FBChannel::FB_DEPTH_BIT:
|
|
srcTex = src->depthStencilTex;
|
|
dstTex = dst->depthStencilTex;
|
|
break;
|
|
}
|
|
_assert_(srcTex && dstTex);
|
|
|
|
// TODO: Check for level too!
|
|
if (width == src->Width() && width == dst->Width() && height == src->Height() && height == dst->Height() && x == 0 && y == 0 && z == 0 && dstX == 0 && dstY == 0 && dstZ == 0) {
|
|
// Don't need to specify region. This might be faster, too.
|
|
context_->CopyResource(dstTex, srcTex);
|
|
return;
|
|
}
|
|
|
|
if (channelBit != FBChannel::FB_DEPTH_BIT) {
|
|
// Non-full copies are not supported for the depth channel.
|
|
// Note that we need to clip the source box.
|
|
if (x < 0) {
|
|
width += x; // note that x is negative
|
|
dstX -= x;
|
|
x = 0;
|
|
}
|
|
if (y < 0) {
|
|
height += y; // note that y is negative
|
|
dstY -= y;
|
|
y = 0;
|
|
}
|
|
if (x + width > src->Width()) {
|
|
width = src->Width() - x;
|
|
}
|
|
if (y + height > src->Height()) {
|
|
height = src->Height() - y;
|
|
}
|
|
D3D11_BOX srcBox{ (UINT)x, (UINT)y, (UINT)z, (UINT)(x + width), (UINT)(y + height), (UINT)(z + depth) };
|
|
context_->CopySubresourceRegion(dstTex, dstLevel, dstX, dstY, dstZ, srcTex, level, &srcBox);
|
|
}
|
|
}
|
|
|
|
bool D3D11DrawContext::BlitFramebuffer(Framebuffer *srcfb, int srcX1, int srcY1, int srcX2, int srcY2, Framebuffer *dstfb, int dstX1, int dstY1, int dstX2, int dstY2, int channelBits, FBBlitFilter filter, const char *tag) {
|
|
// Unfortunately D3D11 has no equivalent to this, gotta render a quad. Well, in some cases we can issue a copy instead.
|
|
Crash();
|
|
return false;
|
|
}
|
|
|
|
bool D3D11DrawContext::CopyFramebufferToMemory(Framebuffer *src, int channelBits, int bx, int by, int bw, int bh, Draw::DataFormat destFormat, void *pixels, int pixelStride, ReadbackMode mode, const char *tag) {
|
|
D3D11Framebuffer *fb = (D3D11Framebuffer *)src;
|
|
|
|
if (fb) {
|
|
_assert_(fb->colorFormat == DXGI_FORMAT_R8G8B8A8_UNORM);
|
|
|
|
// TODO: Figure out where the badness really comes from.
|
|
if (bx + bw > fb->Width()) {
|
|
bw -= (bx + bw) - fb->Width();
|
|
}
|
|
if (by + bh > fb->Height()) {
|
|
bh -= (by + bh) - fb->Height();
|
|
}
|
|
}
|
|
|
|
if (bh <= 0 || bw <= 0)
|
|
return true;
|
|
|
|
bool useGlobalPacktex = (bx + bw <= 512 && by + bh <= 512) && channelBits == FB_COLOR_BIT;
|
|
|
|
ID3D11Texture2D *packTex = nullptr;
|
|
if (!useGlobalPacktex) {
|
|
D3D11_TEXTURE2D_DESC packDesc{};
|
|
packDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
|
|
packDesc.BindFlags = 0;
|
|
packDesc.Width = bw;
|
|
packDesc.Height = bh;
|
|
packDesc.ArraySize = 1;
|
|
packDesc.MipLevels = 1;
|
|
packDesc.Usage = D3D11_USAGE_STAGING;
|
|
packDesc.SampleDesc.Count = 1;
|
|
switch (channelBits) {
|
|
case FB_COLOR_BIT:
|
|
packDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; // TODO: fb->colorFormat;
|
|
break;
|
|
case FB_DEPTH_BIT:
|
|
case FB_STENCIL_BIT:
|
|
if (!fb) {
|
|
// Not supported.
|
|
return false;
|
|
}
|
|
packDesc.Format = fb->depthStencilFormat;
|
|
break;
|
|
default:
|
|
_assert_(false);
|
|
}
|
|
device_->CreateTexture2D(&packDesc, nullptr, &packTex);
|
|
} else {
|
|
switch (channelBits) {
|
|
case FB_DEPTH_BIT:
|
|
case FB_STENCIL_BIT:
|
|
if (!fb)
|
|
return false;
|
|
default:
|
|
break;
|
|
}
|
|
packTex = packTexture_;
|
|
}
|
|
|
|
if (!packTex)
|
|
return false;
|
|
|
|
D3D11_BOX srcBox{ (UINT)bx, (UINT)by, 0, (UINT)(bx + bw), (UINT)(by + bh), 1 };
|
|
DataFormat srcFormat = DataFormat::UNDEFINED;
|
|
switch (channelBits) {
|
|
case FB_COLOR_BIT:
|
|
context_->CopySubresourceRegion(packTex, 0, bx, by, 0, fb ? fb->colorTex : bbRenderTargetTex_, 0, &srcBox);
|
|
srcFormat = DataFormat::R8G8B8A8_UNORM;
|
|
break;
|
|
case FB_DEPTH_BIT:
|
|
case FB_STENCIL_BIT:
|
|
// For depth/stencil buffers, we can't reliably copy subrectangles, so just copy the whole resource.
|
|
_assert_(fb); // Can't copy depth/stencil from backbuffer. Shouldn't happen thanks to checks above.
|
|
context_->CopyResource(packTex, fb->depthStencilTex);
|
|
srcFormat = Draw::DataFormat::D24_S8;
|
|
break;
|
|
default:
|
|
_assert_(false);
|
|
break;
|
|
}
|
|
|
|
// Ideally, we'd round robin between two packTexture_, and simply use the other one. Though if the game
|
|
// does a once-off copy, that won't work at all.
|
|
|
|
// BIG GPU STALL
|
|
D3D11_MAPPED_SUBRESOURCE map;
|
|
HRESULT result = context_->Map(packTex, 0, D3D11_MAP_READ, 0, &map);
|
|
if (FAILED(result)) {
|
|
return false;
|
|
}
|
|
|
|
const size_t srcByteOffset = by * map.RowPitch + bx * DataFormatSizeInBytes(srcFormat);
|
|
const uint8_t *srcWithOffset = (const uint8_t *)map.pData + srcByteOffset;
|
|
switch (channelBits) {
|
|
case FB_COLOR_BIT:
|
|
// Pixel size always 4 here because we always request BGRA8888.
|
|
ConvertFromRGBA8888((uint8_t *)pixels, srcWithOffset, pixelStride, map.RowPitch / sizeof(uint32_t), bw, bh, destFormat);
|
|
break;
|
|
case FB_DEPTH_BIT:
|
|
if (srcFormat == destFormat) {
|
|
// Can just memcpy when it matches no matter the format!
|
|
uint8_t *dst = (uint8_t *)pixels;
|
|
const uint8_t *src = (const uint8_t *)srcWithOffset;
|
|
for (int y = 0; y < bh; ++y) {
|
|
memcpy(dst, src, bw * DataFormatSizeInBytes(srcFormat));
|
|
dst += pixelStride * DataFormatSizeInBytes(srcFormat);
|
|
src += map.RowPitch;
|
|
}
|
|
} else if (destFormat == DataFormat::D32F) {
|
|
ConvertToD32F((uint8_t *)pixels, srcWithOffset, pixelStride, map.RowPitch / sizeof(uint32_t), bw, bh, srcFormat);
|
|
} else if (destFormat == DataFormat::D16) {
|
|
ConvertToD16((uint8_t *)pixels, srcWithOffset, pixelStride, map.RowPitch / sizeof(uint32_t), bw, bh, srcFormat);
|
|
} else {
|
|
_assert_(false);
|
|
}
|
|
break;
|
|
case FB_STENCIL_BIT:
|
|
if (srcFormat == destFormat) {
|
|
// Can just memcpy when it matches no matter the format!
|
|
uint8_t *dst = (uint8_t *)pixels;
|
|
const uint8_t *src = (const uint8_t *)srcWithOffset;
|
|
for (int y = 0; y < bh; ++y) {
|
|
memcpy(dst, src, bw * DataFormatSizeInBytes(srcFormat));
|
|
dst += pixelStride * DataFormatSizeInBytes(srcFormat);
|
|
src += map.RowPitch;
|
|
}
|
|
} else if (destFormat == DataFormat::S8) {
|
|
for (int y = 0; y < bh; y++) {
|
|
uint8_t *destStencil = (uint8_t *)pixels + y * pixelStride;
|
|
const uint32_t *src = (const uint32_t *)(srcWithOffset + map.RowPitch * y);
|
|
for (int x = 0; x < bw; x++) {
|
|
destStencil[x] = src[x] >> 24;
|
|
}
|
|
}
|
|
} else {
|
|
_assert_(false);
|
|
}
|
|
break;
|
|
}
|
|
|
|
context_->Unmap(packTex, 0);
|
|
|
|
if (!useGlobalPacktex) {
|
|
packTex->Release();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void D3D11DrawContext::BindFramebufferAsRenderTarget(Framebuffer *fbo, const RenderPassInfo &rp, const char *tag) {
|
|
// TODO: deviceContext1 can actually discard. Useful on Windows Mobile.
|
|
if (fbo) {
|
|
D3D11Framebuffer *fb = (D3D11Framebuffer *)fbo;
|
|
if (curRenderTargetView_ == fb->colorRTView && curDepthStencilView_ == fb->depthStencilRTView) {
|
|
// No need to switch, but let's fallthrough to clear!
|
|
} else {
|
|
// It's not uncommon that the first slot happens to have the new render target bound as a texture,
|
|
// so unbind to make the validation layers happy.
|
|
ID3D11ShaderResourceView *empty[1] = {};
|
|
context_->PSSetShaderResources(0, ARRAY_SIZE(empty), empty);
|
|
context_->OMSetRenderTargets(1, &fb->colorRTView, fb->depthStencilRTView);
|
|
curRenderTargetView_ = fb->colorRTView;
|
|
curDepthStencilView_ = fb->depthStencilRTView;
|
|
curRTWidth_ = fb->Width();
|
|
curRTHeight_ = fb->Height();
|
|
}
|
|
curRenderTarget_ = fb;
|
|
} else {
|
|
if (curRenderTargetView_ == bbRenderTargetView_ && curDepthStencilView_ == bbDepthStencilView_) {
|
|
// No need to switch, but let's fallthrough to clear!
|
|
} else {
|
|
context_->OMSetRenderTargets(1, &bbRenderTargetView_, bbDepthStencilView_);
|
|
curRenderTargetView_ = bbRenderTargetView_;
|
|
curDepthStencilView_ = bbDepthStencilView_;
|
|
curRTWidth_ = bbWidth_;
|
|
curRTHeight_ = bbHeight_;
|
|
}
|
|
curRenderTarget_ = nullptr;
|
|
}
|
|
if (rp.color == RPAction::CLEAR && curRenderTargetView_) {
|
|
float cv[4]{};
|
|
if (rp.clearColor)
|
|
Uint8x4ToFloat4(cv, rp.clearColor);
|
|
context_->ClearRenderTargetView(curRenderTargetView_, cv);
|
|
}
|
|
int mask = 0;
|
|
if (rp.depth == RPAction::CLEAR) {
|
|
mask |= D3D11_CLEAR_DEPTH;
|
|
}
|
|
if (rp.stencil == RPAction::CLEAR) {
|
|
mask |= D3D11_CLEAR_STENCIL;
|
|
}
|
|
if (mask && curDepthStencilView_) {
|
|
context_->ClearDepthStencilView(curDepthStencilView_, mask, rp.clearDepth, rp.clearStencil);
|
|
}
|
|
|
|
if (invalidationCallback_) {
|
|
invalidationCallback_(InvalidationCallbackFlags::RENDER_PASS_STATE);
|
|
}
|
|
}
|
|
|
|
void D3D11DrawContext::BindFramebufferAsTexture(Framebuffer *fbo, int binding, FBChannel channelBit, int layer) {
|
|
_dbg_assert_(binding < MAX_BOUND_TEXTURES);
|
|
_dbg_assert_(layer == ALL_LAYERS || layer == 0); // No multiple layer support on D3D
|
|
D3D11Framebuffer *fb = (D3D11Framebuffer *)fbo;
|
|
switch (channelBit) {
|
|
case FBChannel::FB_COLOR_BIT:
|
|
context_->PSSetShaderResources(binding, 1, &fb->colorSRView);
|
|
break;
|
|
case FBChannel::FB_DEPTH_BIT:
|
|
if (fb->depthSRView) {
|
|
context_->PSSetShaderResources(binding, 1, &fb->depthSRView);
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
uint64_t D3D11DrawContext::GetNativeObject(NativeObject obj, void *srcObject) {
|
|
switch (obj) {
|
|
case NativeObject::DEVICE:
|
|
return (uint64_t)(uintptr_t)device_;
|
|
case NativeObject::CONTEXT:
|
|
return (uint64_t)(uintptr_t)context_;
|
|
case NativeObject::DEVICE_EX:
|
|
return (uint64_t)(uintptr_t)device1_;
|
|
case NativeObject::CONTEXT_EX:
|
|
return (uint64_t)(uintptr_t)context1_;
|
|
case NativeObject::BACKBUFFER_COLOR_TEX:
|
|
return (uint64_t)(uintptr_t)bbRenderTargetTex_;
|
|
case NativeObject::BACKBUFFER_DEPTH_TEX:
|
|
return (uint64_t)(uintptr_t)bbDepthStencilTex_;
|
|
case NativeObject::BACKBUFFER_COLOR_VIEW:
|
|
return (uint64_t)(uintptr_t)bbRenderTargetView_;
|
|
case NativeObject::BACKBUFFER_DEPTH_VIEW:
|
|
return (uint64_t)(uintptr_t)bbDepthStencilView_;
|
|
case NativeObject::FEATURE_LEVEL:
|
|
return (uint64_t)(uintptr_t)featureLevel_;
|
|
case NativeObject::TEXTURE_VIEW:
|
|
return (uint64_t)(((D3D11Texture *)srcObject)->View());
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
void D3D11DrawContext::GetFramebufferDimensions(Framebuffer *fbo, int *w, int *h) {
|
|
D3D11Framebuffer *fb = (D3D11Framebuffer *)fbo;
|
|
if (fb) {
|
|
*w = fb->Width();
|
|
*h = fb->Height();
|
|
} else {
|
|
*w = bbWidth_;
|
|
*h = bbHeight_;
|
|
}
|
|
}
|
|
|
|
DrawContext *T3DCreateD3D11Context(ID3D11Device *device, ID3D11DeviceContext *context, ID3D11Device1 *device1, ID3D11DeviceContext1 *context1, IDXGISwapChain *swapChain, D3D_FEATURE_LEVEL featureLevel, HWND hWnd, const std::vector<std::string> &adapterNames, int maxInflightFrames) {
|
|
return new D3D11DrawContext(device, context, device1, context1, swapChain, featureLevel, hWnd, adapterNames, maxInflightFrames);
|
|
}
|
|
|
|
} // namespace Draw
|