/**************************************************************************** Copyright (C) 2007 Radon Labs GmbH Copyright (c) 2011-2013,WebJet Business Division,CYOU http://www.genesis-3d.com.cn Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ****************************************************************************/ //------------------------------------------------------------------------------ // d3d9rendertarget.cc // (C) 2007 Radon Labs GmbH //------------------------------------------------------------------------------ #if WIN32 #include "stdneb.h" #include "profilesystem/ProfileSystem.h" #include "RenderTargetD3D9.h" #include "RenderDeviceD3D9.h" #include "D3D9Types.h" namespace D3D9 { __ImplementClass(D3D9::RenderTargetD3D9, 'D9RT', RenderBase::RenderTarget); //------------------------------------------------------------------------------ /** */ RenderTargetD3D9::RenderTargetD3D9() : d3d9RenderTarget(0), d3d9DepthStencil(0), d3d9ResolveTexture(0), d3d9CPUResolveTexture(0), d3d9swapChain(0), d3d9MultiSampleType(D3DMULTISAMPLE_NONE), d3d9MultiSampleQuality(0), d3d9ColorBufferFormat(D3DFMT_UNKNOWN), needsResolve(false), d3dpoolType(D3DPOOL_MANAGED) { // empty } //------------------------------------------------------------------------------ /** */ RenderTargetD3D9::~RenderTargetD3D9() { Discard(); n_assert(!this->isValid); n_assert(0 == this->d3d9RenderTarget); n_assert(0 == this->d3d9DepthStencil); n_assert(0 == this->d3d9ResolveTexture); } //------------------------------------------------------------------------------ /** */ void RenderTargetD3D9::LoadBuffers(SizeT _width, SizeT _height) { //todo : need not to set width && height .must be set before here n_assert(0 == this->d3d9RenderTarget); n_assert(0 == this->d3d9DepthStencil); n_assert(0 == this->d3d9ResolveTexture); HRESULT hr; RenderDeviceD3D9* device = RenderDeviceD3D9::Instance(); memorySize = 0; // if we're the default render target, query display device // for setup parameters if (this->isDefaultRenderTarget) { // NOTE: the default render target will never be anti-aliased! // this assumes a render pipeline where the actual rendering goes // into an offscreen render target and is then resolved to the back buffer this->SetWidth(_width); this->SetHeight(_height); this->SetAntiAliasQuality(AntiAliasQuality::None); this->SetColorBufferFormat(PixelFormat::X8R8G8B8); } if (!this->isDefaultRenderTarget && screenSizeRatio > 0.f) { if (this->resolveTextureDimensionsValid) { if ((this->resolveTextureWidth != this->width) || (this->resolveTextureHeight != this->height)) { //do nothing } else { this->SetResolveTextureWidth(SizeT(_width*screenSizeRatio)); this->SetResolveTextureHeight(SizeT(_height*screenSizeRatio)); this->resolveRect.right = int(_width*screenSizeRatio); this->resolveRect.bottom = int(_height*screenSizeRatio); } } this->SetWidth(int(_width*screenSizeRatio)); this->SetHeight(int(_height*screenSizeRatio)); } // setup our pixel format and multisample parameters (order important!) this->d3d9ColorBufferFormat = D3D9Types::AsD3D9PixelFormat(this->colorBufferFormat); this->SetupMultiSampleType(); // check if a resolve texture must be allocated if (this->mipMapsEnabled || (D3DMULTISAMPLE_NONE != this->d3d9MultiSampleType) || (this->resolveTextureDimensionsValid && ((this->resolveTextureWidth != this->width) || (this->resolveTextureHeight != this->height)))) { this->needsResolve = true; } else { this->needsResolve = false; } // create the render target either as a texture, or as // a surface, or don't create it if rendering goes // into backbuffer if (!this->needsResolve) { if (!this->isDefaultRenderTarget) { DWORD usage = D3DUSAGE_RENDERTARGET; D3DPOOL d3dpoolType = D3DPOOL_DEFAULT; // inplace resolve texture can be used (render target == resolve texture) hr = device->CreateTexture(this->width, // Width this->height, // Height 1, // Levels usage, // Usage this->d3d9ColorBufferFormat, // Format d3dpoolType, // Pool &(this->d3d9ResolveTexture), // ppTexture NULL); // pSharedHandle n_assert(SUCCEEDED(hr)); n_assert(0 != this->d3d9ResolveTexture); // get the actual render target surface of the texture hr = this->d3d9ResolveTexture->GetSurfaceLevel(0, &(this->d3d9RenderTarget)); n_assert(SUCCEEDED(hr)); memorySize += PixelFormat::GetMemorySize( width, height, 1, GetColorBufferFormat() ); // create extra cpu texture for cpu look up (read-only) if (this->resolveCpuAccess) { usage = D3DUSAGE_DYNAMIC; d3dpoolType = D3DPOOL_SYSTEMMEM; hr = device->CreateTexture(this->width, // Width this->height, // Height 1, // Levels usage, // Usage this->d3d9ColorBufferFormat, // Format d3dpoolType, // Pool &(this->d3d9CPUResolveTexture), // ppTexture NULL); // pSharedHandle n_assert(SUCCEEDED(hr)); n_assert(0 != this->d3d9CPUResolveTexture); memorySize += PixelFormat::GetMemorySize( width, height, 1, GetColorBufferFormat() ); } } else { // NOTE: if we are the default render target and not antialiased, // rendering will go directly to the backbuffer, so there's no // need to allocate a render target D3DPRESENT_PARAMETERS displayPresentParams = device->GetDefaultPresentParamters(); displayPresentParams.BackBufferWidth = _width; displayPresentParams.BackBufferHeight = _height; displayPresentParams.BackBufferFormat = D3D9Types::AsD3D9PixelFormat(GetColorBufferFormat()); device->CreateAdditionalSwapChain(&displayPresentParams,&d3d9swapChain); } } else { // need to create an extra resolve texture, create the // actual render target directly as surface hr = device->CreateRenderTarget(this->width, // Width this->height, // Height this->d3d9ColorBufferFormat, // Format this->d3d9MultiSampleType, // MultiSample this->d3d9MultiSampleQuality, // MultisampleQuality FALSE, // Lockable &(this->d3d9RenderTarget), // ppSurface NULL); // pSharedHandle n_assert(SUCCEEDED(hr)); n_assert(0 != this->d3d9RenderTarget); // create the resolve texture SizeT resolveWidth = this->resolveTextureDimensionsValid ? this->resolveTextureWidth : this->width; SizeT resolveHeight = this->resolveTextureDimensionsValid ? this->resolveTextureHeight : this->height; memorySize += PixelFormat::GetMemorySize( width, height, 1, GetColorBufferFormat() ); DWORD usage = D3DUSAGE_RENDERTARGET; d3dpoolType = D3DPOOL_DEFAULT; if (this->mipMapsEnabled) { usage |= D3DUSAGE_AUTOGENMIPMAP; } hr = device->CreateTexture(resolveWidth, // Width resolveHeight, // Height 1, // Levels usage, // Usage this->d3d9ColorBufferFormat, // Format d3dpoolType, // Pool &(this->d3d9ResolveTexture), // ppTexture NULL); // pSharedHandle n_assert(SUCCEEDED(hr)); n_assert(0 != this->d3d9ResolveTexture); memorySize += PixelFormat::GetMemorySize( resolveWidth, resolveHeight, 1, GetColorBufferFormat() ); // create extra cpu texture for cpu look up (read-only) if (this->resolveCpuAccess) { usage = D3DUSAGE_DYNAMIC; d3dpoolType = D3DPOOL_SYSTEMMEM; hr = device->CreateTexture(resolveWidth, // Width resolveHeight, // Height 1, // Levels usage, // Usage this->d3d9ColorBufferFormat, // Format d3dpoolType, // Pool &(this->d3d9CPUResolveTexture), // ppTexture NULL); // pSharedHandle n_assert(SUCCEEDED(hr)); n_assert(0 != this->d3d9CPUResolveTexture); memorySize += PixelFormat::GetMemorySize( resolveWidth, resolveHeight, 1, GetColorBufferFormat() ); } } // create the depth/stencil buffer if needed if (this->HasDepthStencilBuffer()) { if (sharedDepthStencilBufferTarget.isvalid()) { GPtr pSharedDepthStencilBufferTarget = this->sharedDepthStencilBufferTarget.downcast(); n_assert(0 != pSharedDepthStencilBufferTarget->d3d9DepthStencil); n_assert(this->width == pSharedDepthStencilBufferTarget->width); n_assert(this->height == pSharedDepthStencilBufferTarget->height); n_assert(this->antiAliasQuality == pSharedDepthStencilBufferTarget->antiAliasQuality); this->d3d9DepthStencil = pSharedDepthStencilBufferTarget->d3d9DepthStencil; this->d3d9DepthStencil->AddRef(); } else { D3DFORMAT dephFormat = D3DFMT_D24S8; hr = device->CreateDepthStencilSurface(this->width, // Width this->height, // Height dephFormat, // Format this->d3d9MultiSampleType, // MultiSample this->d3d9MultiSampleQuality, // MultisampleQuality TRUE, // Discard &(this->d3d9DepthStencil), // ppSurface NULL); // pSharedHandle n_assert(SUCCEEDED(hr)); n_assert(0 != this->d3d9DepthStencil); memorySize += PixelFormat::GetMemorySize( width, height, 1, D3D9Types::AsGenesisPixelFormat(dephFormat)); } } Super::setup(); } void RenderTargetD3D9::AssignD3D9Textures(bool bIsLostDevice) { // if a resolve texture exists, create a shared texture resource, so that // the texture is publicly visible if ((0 != this->d3d9ResolveTexture)) { if (!bIsLostDevice) { this->resolveTexture = TextureD3D9::Create(); } n_assert(this->resolveTexture.isvalid()) this->resolveTexture->Setup(); this->resolveTexture.downcast()->SetupFromD3D9Texture(this->d3d9ResolveTexture); this->d3d9ResolveTexture->AddRef(); } // if a cpu resolve texture exists, create a shared texture resource, so that // the texture is usable for a cpu lockup if ((0 != this->d3d9CPUResolveTexture)/* && this->resolveTextureResId.IsValid()*/) { if (!bIsLostDevice) { this->resolveCPUTexture = TextureD3D9::Create(); } n_assert(this->resolveCPUTexture.isvalid()) this->resolveCPUTexture->SetUsage(Texture::UsageDynamic); this->resolveCPUTexture->SetAccess(Texture::AccessRead); this->resolveCPUTexture.downcast()->SetupFromD3D9Texture(this->d3d9CPUResolveTexture); this->d3d9CPUResolveTexture->AddRef(); } } //------------------------------------------------------------------------------ /** */ void RenderTargetD3D9::Discard() { if (0 != this->d3d9RenderTarget) { this->d3d9RenderTarget->Release(); this->d3d9RenderTarget = 0; } if (0 != this->d3d9DepthStencil) { this->d3d9DepthStencil->Release(); this->d3d9DepthStencil = 0; } if (0 != this->d3d9ResolveTexture) { this->d3d9ResolveTexture->Release(); this->d3d9ResolveTexture = 0; } if (0 != this->d3d9CPUResolveTexture) { this->d3d9CPUResolveTexture->Release(); this->d3d9CPUResolveTexture = 0; } if (0 != this->d3d9swapChain) { this->d3d9swapChain->Release(); this->d3d9swapChain = 0; } Super::Discard(); } //------------------------------------------------------------------------------ /** Select the antialias parameters that most closely resembly the preferred settings in the DisplayDevice object. */ void RenderTargetD3D9::SetupMultiSampleType() { n_assert(D3DFMT_UNKNOWN != this->d3d9ColorBufferFormat); RenderDeviceD3D9* renderDevice = RenderDeviceD3D9::Instance(); #if NEBULA3_DIRECT3D_DEBUG this->d3d9MultiSampleType = D3DMULTISAMPLE_NONE; this->d3d9MultiSampleQuality = 0; #else // convert Nebula3 antialias quality into D3D type this->d3d9MultiSampleType = D3D9Types::AsD3D9MultiSampleType(this->antiAliasQuality); // check if the multisample type is compatible with the selected display mode DWORD availableQualityLevels = 0; HRESULT renderTargetResult = renderDevice->CheckDeviceMultiSampleType(0, D3DDEVTYPE_HAL, this->d3d9ColorBufferFormat, FALSE, this->d3d9MultiSampleType, &availableQualityLevels); HRESULT depthBufferResult = renderDevice->CheckDeviceMultiSampleType(0, D3DDEVTYPE_HAL, D3DFMT_D24S8, FALSE, this->d3d9MultiSampleType, NULL); if ((D3DERR_NOTAVAILABLE == renderTargetResult) || (D3DERR_NOTAVAILABLE == depthBufferResult)) { // reset to no multisampling this->d3d9MultiSampleType = D3DMULTISAMPLE_NONE; this->d3d9MultiSampleQuality = 0; } else { n_assert(SUCCEEDED(renderTargetResult) && SUCCEEDED(depthBufferResult)); } // clamp multisample quality to the available quality levels if (availableQualityLevels > 0) { this->d3d9MultiSampleQuality = availableQualityLevels - 1; } else { this->d3d9MultiSampleQuality = 0; } #endif } //------------------------------------------------------------------------------ /** */ void RenderTargetD3D9::EndPass() { HRESULT hr; RenderDeviceD3D9* renderDevice = RenderDeviceD3D9::Instance(); // if necessary need to resolve the render target, either // into our resolve texture, or into the back buffer if (this->needsResolve) { RECT destRect; CONST RECT* pDestRect = NULL; if (this->resolveRectValid) { destRect.left = this->resolveRect.left; destRect.right = this->resolveRect.right; destRect.top = this->resolveRect.top; destRect.bottom = this->resolveRect.bottom; pDestRect = &destRect; } IDirect3DSurface9* resolveSurface = 0; hr = this->d3d9ResolveTexture->GetSurfaceLevel(0, &resolveSurface); hr = renderDevice->StretchRect(this->d3d9RenderTarget, NULL, resolveSurface, pDestRect, D3DTEXF_NONE); n_assert(SUCCEEDED(hr)); // need cpu access, copy from gpu mem to sys mem if (this->resolveCpuAccess) { HRESULT hr; D3DLOCKED_RECT dstLockRect; D3DLOCKED_RECT srcLockRect; IDirect3DSurface9* dstSurface = 0; hr = this->d3d9CPUResolveTexture->GetSurfaceLevel(0, &dstSurface); n_assert(SUCCEEDED(hr)); hr = dstSurface->LockRect(&dstLockRect, 0, 0); n_assert(SUCCEEDED(hr)); hr = resolveSurface->LockRect(&srcLockRect, 0, D3DLOCK_READONLY); n_assert(SUCCEEDED(hr)); Memory::Copy(srcLockRect.pBits, dstLockRect.pBits, dstLockRect.Pitch * this->resolveCPUTexture->GetWidth()); dstSurface->Release(); } resolveSurface->Release(); } else if (this->resolveCpuAccess) { HRESULT hr; // copy data IDirect3DSurface9* resolveSurface = 0; hr = this->d3d9CPUResolveTexture->GetSurfaceLevel(0, &resolveSurface); n_assert(SUCCEEDED(hr)); hr = renderDevice->GetRenderTargetData(this->d3d9RenderTarget, resolveSurface); n_assert(SUCCEEDED(hr)); resolveSurface->Release(); } // unset multiple rendertargets if (this->mrtIndex > 0) { renderDevice->SetRenderTarget(this->mrtIndex, 0); } } //------------------------------------------------------------------------------ /** */ void RenderTargetD3D9::GenerateMipLevels() { n_assert(0 != this->d3d9ResolveTexture); n_assert(this->mipMapsEnabled); this->d3d9ResolveTexture->GenerateMipSubLevels(); } //------------------------------------------------------------------------------ /** */ void RenderTargetD3D9::OnDeviceLost() { if (this->sharedDepthStencilBufferTarget.isvalid()) { this->sharedDepthStencilBufferTarget.downcast()->OnDeviceLost(); } if (this->resolveTexture.isvalid()) { this->resolveTexture.downcast()->OnDeviceLost(); } if (this->resolveCPUTexture.isvalid()) { resolveCPUTexture.downcast()->OnDeviceLost(); } if (this->resolveDepthTexture.isvalid()) { resolveDepthTexture.downcast()->OnDeviceLost(); } if (0 != this->d3d9RenderTarget) { this->d3d9RenderTarget->Release(); this->d3d9RenderTarget = 0; } if (0 != this->d3d9DepthStencil) { this->d3d9DepthStencil->Release(); this->d3d9DepthStencil = 0; } if (0 != this->d3d9ResolveTexture) { this->d3d9ResolveTexture->Release(); this->d3d9ResolveTexture = 0; } if (0 != this->d3d9CPUResolveTexture) { this->d3d9CPUResolveTexture->Release(); this->d3d9CPUResolveTexture = 0; } if (0 != this->d3d9swapChain) { this->d3d9swapChain->Release(); this->d3d9swapChain = 0; } this->isValid = false; } //------------------------------------------------------------------------------ /** */ void RenderTargetD3D9::OnDeviceReset() { LoadBuffers(GetWidth(),GetHeight()); AssignD3D9Textures(true); } //------------------------------------------------------------------------------ /** */ void RenderTargetD3D9::CopyFrom(const Math::float4& srcRect, const GPtrsrcRT, const Math::float4& desRect) { GPtr srt = srcRT.downcast(); n_assert(srt); HRESULT hr; RenderDeviceD3D9* renderDevice = RenderDeviceD3D9::Instance(); RECT srcRECT; srcRECT.left = LONG(srcRect.x()); srcRECT.top = LONG(srcRect.y()); srcRECT.right = LONG(srcRect.z()); srcRECT.bottom = LONG(srcRect.w()); RECT desRECT; desRECT.left = LONG(desRect.x()); desRECT.top = LONG(desRect.y()); desRECT.right = LONG(desRect.z()); desRECT.bottom = LONG(desRect.w()); hr = renderDevice->StretchRect(srt->GetD3DRT(),&srcRECT,d3d9RenderTarget,&desRECT,D3DTEXF_NONE); n_assert(SUCCEEDED(hr)); } } // namespace D3D9 #endif