#include "DeviceD3D11.h" #include "GeometryD3D11.h" #include "ShaderD3D11.h" #include "TextureD3D11.h" #include "FramebufferD3D11.h" #include "rbx/rbxTime.h" #include "rbx/Profiler.h" #include "StringConv.h" #include "HeadersD3D11.h" LOGGROUP(Graphics) LOGGROUP(VR) FASTFLAGVARIABLE(DebugD3D11DebugMode, false) namespace RBX { namespace Graphics { static unsigned int getMaxSamplesSupported(ID3D11Device* device11) { unsigned int result = 1; for (unsigned int mode = 2; mode <= D3D11_MAX_MULTISAMPLE_SAMPLE_COUNT; mode *= 2) { unsigned maxQualityLevel; HRESULT hr = device11->CheckMultisampleQualityLevels(DXGI_FORMAT_R8G8B8A8_UNORM, mode, &maxQualityLevel); RBXASSERT(SUCCEEDED(hr)); if (maxQualityLevel <= 0) break; result = mode; } return result; } static DXGI_ADAPTER_DESC getAdapterDesc(IDXGIDevice* device) { IDXGIAdapter* adapter = NULL; device->GetAdapter(&adapter); DXGI_ADAPTER_DESC desc = {}; adapter->GetDesc(&desc); adapter->Release(); return desc; } template T* queryInterface(P* object) { void* result = 0; if (SUCCEEDED(object->QueryInterface(__uuidof(T), &result))) return static_cast(result); return 0; } DeviceD3D11::DeviceD3D11(void* windowHandle) : windowHandle(windowHandle) , device11(NULL) , swapChain11(NULL) , immediateContext(NULL) , frameTimeQueryIssued(false) , gpuTime(0) , beginQuery(NULL) , endQuery(NULL) , disjointQuery(NULL) , vrEnabled(true) { createDevice(); caps = DeviceCaps(); caps.supportsFramebuffer = true; caps.supportsShaders = true; caps.supportsFFP = false; caps.supportsStencil = true; caps.supportsIndex32 = true; caps.supportsTextureDXT = true; caps.supportsTexturePVR = false; caps.supportsTextureHalfFloat = true; //DXGI_FORMAT_R16G16B16A16_FLOAT is always supported on DX11 HW (including featureLvl 10 - 9.3) caps.supportsTexture3D = true; caps.supportsTextureNPOT = shaderProfile == shaderProfile_DX11_level_9_3 ? false : true; caps.supportsTextureETC1 = false; caps.supportsTexturePartialMipChain = true; caps.maxDrawBuffers = shaderProfile == shaderProfile_DX11_level_9_3 ? 4 : 8; caps.maxSamples = getMaxSamplesSupported(device11); caps.maxTextureSize = D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION; caps.maxTextureUnits = 16; caps.colorOrderBGR = false; caps.needsHalfPixelOffset = false; caps.requiresRenderTargetFlipping = false; caps.retina = false; std::pair dimensions = getFramebufferSize(); createMainFramebuffer(dimensions.first, dimensions.second); // queries HRESULT hr; D3D11_QUERY_DESC queryDesc; queryDesc.MiscFlags = 0; queryDesc.Query = D3D11_QUERY_TIMESTAMP; hr = device11->CreateQuery(&queryDesc, &beginQuery); RBXASSERT(SUCCEEDED(hr)); hr = device11->CreateQuery(&queryDesc, &endQuery); RBXASSERT(SUCCEEDED(hr)); queryDesc.Query = D3D11_QUERY_TIMESTAMP_DISJOINT; hr = device11->CreateQuery(&queryDesc, &disjointQuery); RBXASSERT(SUCCEEDED(hr)); if (IDXGIDevice1* deviceDXGI1 = queryInterface(device11)) { deviceDXGI1->SetMaximumFrameLatency(1); deviceDXGI1->Release(); } if (IDXGIDevice* deviceDXGI = queryInterface(device11)) { DXGI_ADAPTER_DESC desc = getAdapterDesc(deviceDXGI); FASTLOGS(FLog::Graphics, "D3D11 Adapter: %s", utf8_encode(desc.Description)); FASTLOG2(FLog::Graphics, "D3D11 Adapter: Vendor %04x Device %04x", desc.VendorId, desc.DeviceId); // Do not use GPU profiling on Intel cards to avoid a crash in Intel driver when releasing resources in gpuShutdown // Also, this vendor id is SO COOL! if (desc.VendorId != 0x8086) Profiler::gpuInit(getImmediateContext11()); deviceDXGI->Release(); } if (vr) { try { vr->setup(this); } catch (RBX::base_exception& e) { FASTLOGS(FLog::VR, "VR ERROR during setup: %s", e.what()); vr.reset(); } } } void DeviceD3D11::createMainFramebuffer(unsigned width, unsigned height) { RBXASSERT(!mainFramebuffer); // Get back buffer, create view ID3D11Texture2D* backBuffer = NULL; HRESULT hr = swapChain11->GetBuffer( 0, __uuidof( ID3D11Texture2D ), ( LPVOID* )&backBuffer ); RBXASSERT(SUCCEEDED(hr)); shared_ptr backBufferRB = shared_ptr(new RenderbufferD3D11(this, Texture::Format_RGBA8, width, height, 1, backBuffer)); std::vector> colorBuffers; colorBuffers.push_back(backBufferRB); // Create depth stencil shared_ptr depthStencil = shared_ptr(new RenderbufferD3D11(this, Texture::Format_D24S8, width, height, 1)); // create frame buffer mainFramebuffer.reset(new FramebufferD3D11(this, colorBuffers, depthStencil)); } DeviceD3D11::~DeviceD3D11() { Profiler::gpuShutdown(); // we want to release all the DX references before releasing device to be able to test if nothing is hanging vr.reset(); immediateContext.reset(); mainFramebuffer.reset(); ReleaseCheck(beginQuery); ReleaseCheck(endQuery); ReleaseCheck(disjointQuery); ReleaseCheck(swapChain11); ReleaseCheck(device11); } void DeviceD3D11::defineGlobalConstants(size_t dataSize, const std::vector& constants) { RBXASSERT(!constants.empty()); // Since constants are directly set to register values, we impose additional restrictions on constant data // The struct should be an integer number of float4 registers, and every constant has to be aligned to float4 boundary RBXASSERT(dataSize % 16 == 0); immediateContext->defineGlobalConstants(dataSize); } DeviceContext* DeviceD3D11::beginFrame() { std::pair dimensions = getFramebufferSize(); // Don't render anything if there is no main framebuffer or device is lost if (!mainFramebuffer) return false; // Don't render anything if window size changed; wait for validate if (dimensions.first != mainFramebuffer->getWidth() || dimensions.second != mainFramebuffer->getHeight()) return NULL; immediateContext->bindFramebuffer(mainFramebuffer.get()); immediateContext->clearStates(); if (disjointQuery && beginQuery && endQuery) { if (!frameTimeQueryIssued) { immediateContext->beginQuery(disjointQuery); immediateContext->endQuery(beginQuery); } } return immediateContext.get(); } bool DeviceD3D11::validate() { std::pair dimensions = getFramebufferSize(); // Don't change anything if window is minimized (getFramebufferSize returns 1x1) if (dimensions.first <= 1 && dimensions.second <= 1) return false; // Reset device if window size changed if (mainFramebuffer && (dimensions.first != mainFramebuffer->getWidth() || dimensions.second != mainFramebuffer->getHeight())) { immediateContext->getContextDX11()->OMSetRenderTargets(NULL, NULL, NULL); mainFramebuffer.reset(); resizeSwapchain(); createMainFramebuffer(dimensions.first, dimensions.second); } return true; } void DeviceD3D11::endFrame() { if (disjointQuery && beginQuery && endQuery) { if (!frameTimeQueryIssued) { immediateContext->endQuery(endQuery); immediateContext->endQuery(disjointQuery); frameTimeQueryIssued = true; } else { UINT64 tsBeginFrame, tsEndFrame; D3D11_QUERY_DATA_TIMESTAMP_DISJOINT tsDisjoint; bool queryFinished = immediateContext->getQueryData(disjointQuery, &tsDisjoint, sizeof(tsDisjoint)); queryFinished &= immediateContext->getQueryData(beginQuery, &tsBeginFrame, sizeof(UINT64)); queryFinished &= immediateContext->getQueryData(endQuery, &tsEndFrame, sizeof(UINT64)); if (queryFinished) { if (!tsDisjoint.Disjoint) gpuTime = (float)(double(tsEndFrame - tsBeginFrame) / double(tsDisjoint.Frequency) * 1000.0); frameTimeQueryIssued = false; } } } if (vr && vrEnabled) { vr->submitFrame(immediateContext.get()); } present(); } DeviceStats DeviceD3D11::getStatistics() const { DeviceStats result = {}; result.gpuFrameTime = gpuTime; return result; } shared_ptr DeviceD3D11::createTexture(Texture::Type type, Texture::Format format, unsigned int width, unsigned int height, unsigned int depth, unsigned int mipLevels, Texture::Usage usage) { return shared_ptr(new TextureD3D11(this, type, format, width, height, depth, mipLevels, usage)); } shared_ptr DeviceD3D11::createVertexBuffer(size_t elementSize, size_t elementCount, GeometryBuffer::Usage usage) { return shared_ptr(new VertexBufferD3D11(this, elementSize, elementCount, usage)); } shared_ptr DeviceD3D11::createIndexBuffer(size_t elementSize, size_t elementCount, GeometryBuffer::Usage usage) { return shared_ptr(new IndexBufferD3D11(this, elementSize, elementCount, usage)); } shared_ptr DeviceD3D11::createVertexLayout(const std::vector& elements) { return shared_ptr(new VertexLayoutD3D11(this, elements)); } shared_ptr DeviceD3D11::createGeometryImpl(const shared_ptr& layout, const std::vector >& vertexBuffers, const shared_ptr& indexBuffer, unsigned int baseVertexIndex) { return shared_ptr(new GeometryD3D11(this, layout, vertexBuffers, indexBuffer, baseVertexIndex)); } shared_ptr DeviceD3D11::createFramebufferImpl(const std::vector >& color, const shared_ptr& depth) { return shared_ptr(new FramebufferD3D11(this, color, depth)); } Framebuffer* DeviceD3D11::getMainFramebuffer() { return mainFramebuffer.get(); } DeviceVR* DeviceD3D11::getVR() { return (vr && vrEnabled) ? vr.get() : NULL; } void DeviceD3D11::setVR(bool enabled) { vrEnabled = enabled; } shared_ptr DeviceD3D11::createRenderbuffer(Texture::Format format, unsigned int width, unsigned int height, unsigned int samples) { return shared_ptr(new RenderbufferD3D11(this, format, width, height, samples)); } std::string DeviceD3D11::createShaderSource(const std::string& path, const std::string& defines, boost::function fileCallback) { std::string dx11Defines = defines; dx11Defines += " DX11"; if (getShaderProfile() == DeviceD3D11::shaderProfile_DX11_level_9_3) dx11Defines += " WIN_MOBILE"; return ShaderProgramD3D11::createShaderSource(path, dx11Defines, this, fileCallback); } std::vector DeviceD3D11::createShaderBytecode(const std::string& source, const std::string& target, const std::string& entrypoint) { return ShaderProgramD3D11::createShaderBytecode(source, target, this, entrypoint); } shared_ptr DeviceD3D11::createVertexShader(const std::vector& bytecode) { return shared_ptr(new VertexShaderD3D11(this, bytecode)); } shared_ptr DeviceD3D11::createFragmentShader(const std::vector& bytecode) { return shared_ptr(new FragmentShaderD3D11(this, bytecode)); } shared_ptr DeviceD3D11::createShaderProgram(const shared_ptr& vertexShader, const shared_ptr& fragmentShader) { return shared_ptr(new ShaderProgramD3D11(this, vertexShader, fragmentShader)); } shared_ptr DeviceD3D11::createShaderProgramFFP() { throw RBX::runtime_error("No FFP support"); } } }