#include "DeviceD3D9.h" #include "GeometryD3D9.h" #include "ShaderD3D9.h" #include "TextureD3D9.h" #include "FramebufferD3D9.h" #include "D3dx9math.h" #include #include "GfxCore/pix.h" FASTFLAG(GraphicsDebugMarkersEnable) namespace RBX { namespace Graphics { static const D3DCULL gCullModeD3D9[RasterizerState::Cull_Count] = { D3DCULL_NONE, D3DCULL_CW, D3DCULL_CCW }; static const D3DBLEND gBlendFactorsD3D9[BlendState::Factor_Count] = { D3DBLEND_ONE, D3DBLEND_ZERO, D3DBLEND_DESTCOLOR, D3DBLEND_SRCALPHA, D3DBLEND_INVSRCALPHA, D3DBLEND_DESTALPHA, D3DBLEND_INVDESTALPHA }; static const D3DCMPFUNC gDepthFuncD3D9[DepthState::Function_Count] = { D3DCMP_ALWAYS, D3DCMP_LESS, D3DCMP_LESSEQUAL }; struct TextureFilterD3D9 { D3DTEXTUREFILTERTYPE min, mag, mip; }; static const TextureFilterD3D9 gSamplerFilterD3D9[SamplerState::Filter_Count] = { { D3DTEXF_POINT, D3DTEXF_POINT, D3DTEXF_POINT }, { D3DTEXF_LINEAR, D3DTEXF_LINEAR, D3DTEXF_LINEAR }, { D3DTEXF_ANISOTROPIC, D3DTEXF_LINEAR, D3DTEXF_LINEAR }, }; static const D3DTEXTUREADDRESS gSamplerAddressD3D9[SamplerState::Address_Count] = { D3DTADDRESS_WRAP, D3DTADDRESS_CLAMP }; template static const typename Map::mapped_type* findKey(const Map& map, const typename Map::key_type& key) { typename Map::const_iterator it = map.find(key); return (it == map.end()) ? NULL : &it->second; } static void transposeMatrix(float* dest, const float* source, const float* lastColumn) { dest[0] = source[0]; dest[1] = source[4]; dest[2] = source[8]; dest[3] = lastColumn[0]; dest[4] = source[1]; dest[5] = source[5]; dest[6] = source[9]; dest[7] = lastColumn[1]; dest[8] = source[2]; dest[9] = source[6]; dest[10] = source[10]; dest[11] = lastColumn[2]; dest[12] = source[3]; dest[13] = source[7]; dest[14] = source[11]; dest[15] = lastColumn[3]; } DeviceContextD3D9::DeviceContextD3D9(Device* device) : device(device) , device9(static_cast(device)->getDevice9()) , globalDataSize(0) , defaultAnisotropy(1) , cachedFramebufferSurfaces(0) , cachedProgram(NULL) , cachedVertexLayout(NULL) , cachedGeometry(NULL) , cachedRasterizerState(RasterizerState::Cull_None) , cachedBlendState(BlendState::Mode_None) , cachedDepthState(DepthState::Function_Always, false) , d3d9(NULL) { pfn_D3DPERF_BeginEvent = 0; pfn_D3DPERF_EndEvent = 0; pfn_D3DPERF_SetMarker = 0; DWORD (WINAPI *pfn_D3DPERF_GetStatus)(); if(PIX_ENABLED) { d3d9 = LoadLibraryA("d3d9.dll"); if (d3d9) { (void*&)pfn_D3DPERF_BeginEvent = GetProcAddress(d3d9, "D3DPERF_BeginEvent"); (void*&)pfn_D3DPERF_EndEvent = GetProcAddress(d3d9, "D3DPERF_EndEvent"); (void*&)pfn_D3DPERF_SetMarker = GetProcAddress(d3d9, "D3DPERF_SetMarker"); (void*&)pfn_D3DPERF_GetStatus = GetProcAddress(d3d9, "D3DPERF_GetStatus"); } } if( !pfn_D3DPERF_GetStatus || !pfn_D3DPERF_GetStatus() ) { FFlag::GraphicsDebugMarkersEnable = false; // no use } } DeviceContextD3D9::~DeviceContextD3D9() { if (d3d9) { FreeLibrary(d3d9); d3d9 = NULL; } } void DeviceContextD3D9::defineGlobalConstants(size_t dataSize) { RBXASSERT(globalDataSize == 0); RBXASSERT(dataSize > 0); globalDataSize = dataSize; } void DeviceContextD3D9::clearStates() { const DeviceCapsD3D9& caps = static_cast(device)->getCapsD3D9(); // Clear framebuffer cache cachedFramebufferSurfaces = 0; // Clear program cache cachedProgram = NULL; // Clear vertex layout cache cachedVertexLayout = NULL; // Clear geometry cache cachedGeometry = NULL; // Clear texture cache for (size_t i = 0; i < ARRAYSIZE(cachedTextureUnits); ++i) { TextureUnit& u = cachedTextureUnits[i]; u.texture = NULL; // Clear state to an invalid value to guarantee a cache miss on the next setup u.samplerState = SamplerState::Filter_Count; } // Clear states to invalid values to guarantee a cache miss on the next setup cachedRasterizerState = RasterizerState(RasterizerState::Cull_Count); cachedBlendState = BlendState(BlendState::Mode_Count); cachedDepthState = DepthState(DepthState::Function_Count, false); } void DeviceContextD3D9::invalidateCachedProgram() { cachedProgram = NULL; } void DeviceContextD3D9::invalidateCachedVertexLayout() { cachedVertexLayout = NULL; } void DeviceContextD3D9::invalidateCachedGeometry() { cachedGeometry = NULL; } void DeviceContextD3D9::invalidateCachedTexture(Texture* texture) { for (unsigned int stage = 0; stage < ARRAYSIZE(cachedTextureUnits); ++stage) { TextureUnit& u = cachedTextureUnits[stage]; if (u.texture == texture) u.texture = NULL; } } void DeviceContextD3D9::setDefaultAnisotropy(unsigned int value) { defaultAnisotropy = value; } void DeviceContextD3D9::updateGlobalConstants(const void* data, size_t dataSize) { RBXASSERT(dataSize == globalDataSize); device9->SetVertexShaderConstantF(0, static_cast(data), globalDataSize / 16); device9->SetPixelShaderConstantF(0, static_cast(data), globalDataSize / 16); } void DeviceContextD3D9::bindFramebuffer(Framebuffer* buffer) { FramebufferD3D9* buffer9 = static_cast(buffer); const std::vector >& color = buffer9->getColor(); const shared_ptr& depth = buffer9->getDepth(); for (size_t i = 1; i < cachedFramebufferSurfaces; ++i) device9->SetRenderTarget(i, NULL); for (size_t i = 0; i < color.size(); ++i) device9->SetRenderTarget(i, static_cast(color[i].get())->getObject()); device9->SetDepthStencilSurface(depth ? static_cast(depth.get())->getObject() : NULL); cachedFramebufferSurfaces = color.size(); } static int colorComponentToInt(float value) { return static_cast(std::max(std::min(value, 1.f), 0.f) * 255.f + 0.5f); } static unsigned int colorToD3D(const float color[4]) { return D3DCOLOR_RGBA(colorComponentToInt(color[0]), colorComponentToInt(color[1]), colorComponentToInt(color[2]), colorComponentToInt(color[3])); } void DeviceContextD3D9::clearFramebuffer(unsigned int mask, const float color[4], float depth, unsigned int stencil) { unsigned int flags = 0; if (mask & Buffer_Color) flags |= D3DCLEAR_TARGET; if (mask & Buffer_Depth) flags |= D3DCLEAR_ZBUFFER; if (mask & Buffer_Stencil) flags |= D3DCLEAR_STENCIL; device9->Clear(0, NULL, flags, (mask & Buffer_Color) ? colorToD3D(color) : 0, depth, stencil); } void DeviceContextD3D9::copyFramebuffer(Framebuffer* buffer, Texture* texture) { RBXASSERT(texture->getType() == Texture::Type_2D); RBXASSERT(texture->getMipLevels() == 1); RBXASSERT(buffer->getWidth() == texture->getWidth() && buffer->getHeight() == texture->getHeight()); IDirect3DSurface9* tempSurface = static_cast(buffer)->grabCopy(); IDirect3DTexture9* textureObject = static_cast(static_cast(texture)->getObject()); D3DSURFACE_DESC surfaceDesc; tempSurface->GetDesc(&surfaceDesc); D3DSURFACE_DESC textureDesc; textureObject->GetLevelDesc(0, &textureDesc); RBXASSERT(textureDesc.Format == surfaceDesc.Format && textureDesc.Width == surfaceDesc.Width && textureDesc.Height == surfaceDesc.Height); D3DLOCKED_RECT surfaceRect = {}; tempSurface->LockRect(&surfaceRect, NULL, 0); D3DLOCKED_RECT textureRect = {}; textureObject->LockRect(0, &textureRect, NULL, 0); for (unsigned int y = 0; y < textureDesc.Height; ++y) { memcpy(static_cast(textureRect.pBits) + textureRect.Pitch * y, static_cast(surfaceRect.pBits) + surfaceRect.Pitch * y, std::min(textureRect.Pitch, surfaceRect.Pitch)); } textureObject->UnlockRect(0); tempSurface->UnlockRect(); unsigned int rc = tempSurface->Release(); RBXASSERT(rc == 0); } void DeviceContextD3D9::resolveFramebuffer(Framebuffer* msaaBuffer, Framebuffer* buffer, unsigned int mask) { RBXASSERT(msaaBuffer->getSamples() > 1); RBXASSERT(buffer->getSamples() == 1); RBXASSERT(msaaBuffer->getWidth() == buffer->getWidth() && msaaBuffer->getHeight() == buffer->getHeight()); FramebufferD3D9* msaaBuffer9 = static_cast(msaaBuffer); FramebufferD3D9* buffer9 = static_cast(buffer); RBXASSERT(msaaBuffer9->getColor().size() == 1); RBXASSERT(buffer9->getColor().size() == 1); RBXASSERT(mask == Buffer_Color); RenderbufferD3D9* msaaBufferColor9 = static_cast(msaaBuffer9->getColor()[0].get()); RenderbufferD3D9* bufferColor9 = static_cast(buffer9->getColor()[0].get()); device9->StretchRect(msaaBufferColor9->getObject(), NULL, bufferColor9->getObject(), NULL, D3DTEXF_LINEAR); } void DeviceContextD3D9::discardFramebuffer(Framebuffer* buffer, unsigned int mask) { } void DeviceContextD3D9::bindProgram(ShaderProgram* program) { if (cachedProgram != program) { cachedProgram = static_cast(program); cachedProgram->bind(); } } void DeviceContextD3D9::setWorldTransforms4x3(const float* data, size_t matrixCount) { cachedProgram->setWorldTransforms4x3(data, matrixCount); } void DeviceContextD3D9::setConstant(int handle, const float* data, size_t vectorCount) { cachedProgram->setConstant(handle, data, vectorCount); } void DeviceContextD3D9::bindTexture(unsigned int stage, Texture* texture, const SamplerState& state) { SamplerState realState = (state.getFilter() == SamplerState::Filter_Anisotropic && state.getAnisotropy() == 0) ? SamplerState(state.getFilter(), state.getAddress(), defaultAnisotropy) : state; RBXASSERT(stage < device->getCaps().maxTextureUnits); RBXASSERT(stage < ARRAYSIZE(cachedTextureUnits)); TextureUnit& u = cachedTextureUnits[stage]; static_cast(texture)->flushChanges(); if (u.texture != texture) { u.texture = static_cast(texture); device9->SetTexture(stage, u.texture->getObject()); } if (u.samplerState != realState) { u.samplerState = realState; device9->SetSamplerState(stage, D3DSAMP_ADDRESSU, gSamplerAddressD3D9[realState.getAddress()]); device9->SetSamplerState(stage, D3DSAMP_ADDRESSV, gSamplerAddressD3D9[realState.getAddress()]); device9->SetSamplerState(stage, D3DSAMP_MINFILTER, gSamplerFilterD3D9[realState.getFilter()].min); device9->SetSamplerState(stage, D3DSAMP_MAGFILTER, gSamplerFilterD3D9[realState.getFilter()].mag); device9->SetSamplerState(stage, D3DSAMP_MIPFILTER, gSamplerFilterD3D9[realState.getFilter()].mip); if (realState.getFilter() == SamplerState::Filter_Anisotropic) device9->SetSamplerState(stage, D3DSAMP_MAXANISOTROPY, realState.getAnisotropy()); } } void DeviceContextD3D9::setRasterizerState(const RasterizerState& state) { if (cachedRasterizerState != state) { cachedRasterizerState = state; device9->SetRenderState(D3DRS_CULLMODE, gCullModeD3D9[state.getCullMode()]); float bias = static_cast(state.getDepthBias()) / static_cast(1 << 24); float slopeBias = static_cast(state.getDepthBias()) / 32.f; // do we need explicit control over slope or just a better formula since these numbers are magic anyway? device9->SetRenderState(D3DRS_DEPTHBIAS, *reinterpret_cast(&bias)); device9->SetRenderState(D3DRS_SLOPESCALEDEPTHBIAS, *reinterpret_cast(&slopeBias)); } } void DeviceContextD3D9::setBlendState(const BlendState& state) { if (cachedBlendState != state) { cachedBlendState = state; if (!state.blendingNeeded()) { device9->SetRenderState(D3DRS_ALPHABLENDENABLE, false); } else { device9->SetRenderState(D3DRS_ALPHABLENDENABLE, true); device9->SetRenderState(D3DRS_SRCBLEND, gBlendFactorsD3D9[state.getColorSrc()]); device9->SetRenderState(D3DRS_DESTBLEND, gBlendFactorsD3D9[state.getColorDst()]); if (state.separateAlphaBlend()) { device9->SetRenderState(D3DRS_SEPARATEALPHABLENDENABLE, true); device9->SetRenderState(D3DRS_SRCBLENDALPHA, gBlendFactorsD3D9[state.getAlphaSrc()]); device9->SetRenderState(D3DRS_DESTBLENDALPHA, gBlendFactorsD3D9[state.getAlphaDst()]); } else device9->SetRenderState(D3DRS_SEPARATEALPHABLENDENABLE, false); } unsigned int colorMask = 0; if (state.getColorMask() & BlendState::Color_R) colorMask |= D3DCOLORWRITEENABLE_RED; if (state.getColorMask() & BlendState::Color_G) colorMask |= D3DCOLORWRITEENABLE_GREEN; if (state.getColorMask() & BlendState::Color_B) colorMask |= D3DCOLORWRITEENABLE_BLUE; if (state.getColorMask() & BlendState::Color_A) colorMask |= D3DCOLORWRITEENABLE_ALPHA; device9->SetRenderState(D3DRS_COLORWRITEENABLE, colorMask); } } void DeviceContextD3D9::setDepthState(const DepthState& state) { if (cachedDepthState != state) { cachedDepthState = state; if (state.getFunction() == DepthState::Function_Always && state.getWrite() == false) { device9->SetRenderState(D3DRS_ZENABLE, false); } else { device9->SetRenderState(D3DRS_ZENABLE, true); device9->SetRenderState(D3DRS_ZFUNC, gDepthFuncD3D9[state.getFunction()]); device9->SetRenderState(D3DRS_ZWRITEENABLE, state.getWrite()); } switch (state.getStencilMode()) { case DepthState::Stencil_None: device9->SetRenderState(D3DRS_STENCILENABLE, false); break; case DepthState::Stencil_IsNotZero: device9->SetRenderState(D3DRS_STENCILENABLE, true); device9->SetRenderState(D3DRS_TWOSIDEDSTENCILMODE, false); device9->SetRenderState(D3DRS_STENCILFUNC, D3DCMP_NOTEQUAL); device9->SetRenderState(D3DRS_STENCILREF, 0); device9->SetRenderState(D3DRS_STENCILFAIL, D3DSTENCILOP_KEEP); device9->SetRenderState(D3DRS_STENCILZFAIL, D3DSTENCILOP_KEEP); device9->SetRenderState(D3DRS_STENCILPASS, D3DSTENCILOP_KEEP); break; case DepthState::Stencil_UpdateZFail: device9->SetRenderState(D3DRS_STENCILENABLE, true); device9->SetRenderState(D3DRS_TWOSIDEDSTENCILMODE, true); device9->SetRenderState(D3DRS_STENCILFUNC, D3DCMP_ALWAYS); device9->SetRenderState(D3DRS_CCW_STENCILFUNC, D3DCMP_ALWAYS); device9->SetRenderState(D3DRS_STENCILFAIL, D3DSTENCILOP_KEEP); device9->SetRenderState(D3DRS_STENCILZFAIL, D3DSTENCILOP_DECR); device9->SetRenderState(D3DRS_STENCILPASS, D3DSTENCILOP_KEEP); device9->SetRenderState(D3DRS_CCW_STENCILFAIL, D3DSTENCILOP_KEEP); device9->SetRenderState(D3DRS_CCW_STENCILZFAIL, D3DSTENCILOP_INCR); device9->SetRenderState(D3DRS_STENCILPASS, D3DSTENCILOP_KEEP); break; default: RBXASSERT(false); } } } void DeviceContextD3D9::drawImpl(Geometry* geometry, Geometry::Primitive primitive, unsigned int offset, unsigned int count, unsigned int indexRangeBegin, unsigned int indexRangeEnd) { static_cast(geometry)->draw(primitive, offset, count, indexRangeBegin, indexRangeEnd, &cachedVertexLayout, &cachedGeometry); } ////////////////////////////////////////////////////////////////////////// // Markers static void ascii2unicode(wchar_t* dest, const char* src, int max) { while( (*dest++ = *src++) && max--) {} } void DeviceContextD3D9::pushDebugMarkerGroup(const char* text) { if (pfn_D3DPERF_BeginEvent) { wchar_t buffer[512]; ascii2unicode(buffer, text, sizeof(buffer)/sizeof(buffer[0]) ); pfn_D3DPERF_BeginEvent(0, buffer); } } void DeviceContextD3D9::popDebugMarkerGroup() { if (pfn_D3DPERF_EndEvent) { pfn_D3DPERF_EndEvent(); } } void DeviceContextD3D9::setDebugMarker(const char* text) { if (pfn_D3DPERF_SetMarker) { wchar_t buffer[512]; ascii2unicode(buffer, text, sizeof(buffer)/sizeof(buffer[0])); pfn_D3DPERF_SetMarker(0, buffer); } } DeviceContextFFPD3D9::DeviceContextFFPD3D9(Device* device) : DeviceContextD3D9(device) { } DeviceContextFFPD3D9::~DeviceContextFFPD3D9() { } void DeviceContextFFPD3D9::updateGlobalConstants(const void* data, size_t dataSize) { const std::map& globalConstants = static_cast(device)->getGlobalConstants(); // we use only viewprojection matrices in shaders. So we'll simple feed view by identity and projection by viewProj D3DXMATRIX matrix; D3DXMatrixIdentity(&matrix); device9->SetTransform(D3DTS_VIEW, &matrix); if (const ShaderGlobalConstant* viewProjection = findKey(globalConstants, "_G.ViewProjection")) { const float* source = reinterpret_cast(static_cast(data) + viewProjection->offset); D3DMATRIX matrix; transposeMatrix(&matrix._11, source, source + 12); device9->SetTransform(D3DTS_PROJECTION, &matrix); } } void DeviceContextFFPD3D9::bindProgram(ShaderProgram* program) { device9->SetRenderState(D3DRS_LIGHTING, false); device9->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_MODULATE); device9->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_CURRENT); device9->SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_CURRENT); } void DeviceContextFFPD3D9::setWorldTransforms4x3(const float* data, size_t matrixCount) { static const float identity[] = {1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0}; static const float lastColumn[] = {0, 0, 0, 1}; D3DMATRIX matrix; transposeMatrix(&matrix._11, matrixCount > 0 ? data : identity, lastColumn); device9->SetTransform(D3DTS_WORLD, &matrix); } void DeviceContextFFPD3D9::setConstant(int handle, const float* data, size_t vectorCount) { if (handle == 0) { device9->SetRenderState(D3DRS_TEXTUREFACTOR, colorToD3D(data)); device9->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_TFACTOR); device9->SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_TFACTOR); } } } }