void ShaderVariation::Release() { if (object_.ptr_ && graphics_) { graphics_->CleanupShaderPrograms(this); if (type_ == VS) { if (graphics_->GetVertexShader() == this) graphics_->SetShaders(0, 0); } else { if (graphics_->GetPixelShader() == this) graphics_->SetShaders(0, 0); } } ATOMIC_SAFE_RELEASE(object_.ptr_); compilerOutput_.Clear(); for (unsigned i = 0; i < MAX_TEXTURE_UNITS; ++i) useTextureUnit_[i] = false; parameters_.Clear(); }
bool IndexBuffer::Create() { Release(); if (!indexCount_) return true; if (graphics_) { D3D11_BUFFER_DESC bufferDesc; memset(&bufferDesc, 0, sizeof bufferDesc); bufferDesc.BindFlags = D3D11_BIND_INDEX_BUFFER; bufferDesc.CPUAccessFlags = dynamic_ ? D3D11_CPU_ACCESS_WRITE : 0; bufferDesc.Usage = dynamic_ ? D3D11_USAGE_DYNAMIC : D3D11_USAGE_DEFAULT; bufferDesc.ByteWidth = (UINT)(indexCount_ * indexSize_); HRESULT hr = graphics_->GetImpl()->GetDevice()->CreateBuffer(&bufferDesc, 0, (ID3D11Buffer**)&object_.ptr_); if (FAILED(hr)) { ATOMIC_SAFE_RELEASE(object_.ptr_); ATOMIC_LOGD3DERROR("Failed to create index buffer", hr); return false; } } return true; }
void IndexBuffer::Release() { Unlock(); if (graphics_ && graphics_->GetIndexBuffer() == this) graphics_->SetIndexBuffer(0); ATOMIC_SAFE_RELEASE(object_.ptr_); }
void VertexBuffer::Release() { Unlock(); if (graphics_) { for (unsigned i = 0; i < MAX_VERTEX_STREAMS; ++i) { if (graphics_->GetVertexBuffer(i) == this) graphics_->SetVertexBuffer(0); } } ATOMIC_SAFE_RELEASE(object_.ptr_); }
bool VertexBuffer::Create() { Release(); if (!vertexCount_ || elements_.Empty()) return true; if (graphics_) { if (graphics_->IsDeviceLost()) { ATOMIC_LOGWARNING("Vertex buffer creation while device is lost"); return true; } unsigned pool = dynamic_ ? D3DPOOL_DEFAULT : D3DPOOL_MANAGED; unsigned d3dUsage = dynamic_ ? D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY : 0; IDirect3DDevice9* device = graphics_->GetImpl()->GetDevice(); HRESULT hr = device->CreateVertexBuffer( vertexCount_ * vertexSize_, d3dUsage, 0, (D3DPOOL)pool, (IDirect3DVertexBuffer9**)&object_.ptr_, 0); if (FAILED(hr)) { ATOMIC_SAFE_RELEASE(object_.ptr_); ATOMIC_LOGD3DERROR("Could not create vertex buffer", hr); return false; } } return true; }
bool IndexBuffer::Create() { Release(); if (!indexCount_) return true; if (graphics_) { if (graphics_->IsDeviceLost()) { ATOMIC_LOGWARNING("Index buffer creation while device is lost"); return true; } unsigned pool = dynamic_ ? D3DPOOL_DEFAULT : D3DPOOL_MANAGED; unsigned d3dUsage = dynamic_ ? D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY : 0; IDirect3DDevice9* device = graphics_->GetImpl()->GetDevice(); HRESULT hr = device->CreateIndexBuffer( indexCount_ * indexSize_, d3dUsage, indexSize_ == sizeof(unsigned) ? D3DFMT_INDEX32 : D3DFMT_INDEX16, (D3DPOOL)pool, (IDirect3DIndexBuffer9**)&object_, 0); if (FAILED(hr)) { ATOMIC_SAFE_RELEASE(object_.ptr_) ATOMIC_LOGD3DERROR("Could not create index buffer", hr); return false; } } return true; }
bool ShaderVariation::Create() { Release(); if (!graphics_) return false; if (!owner_) { compilerOutput_ = "Owner shader has expired"; return false; } // Check for up-to-date bytecode on disk String path, name, extension; SplitPath(owner_->GetName(), path, name, extension); extension = type_ == VS ? ".vs3" : ".ps3"; String binaryShaderName = graphics_->GetShaderCacheDir() + name + "_" + StringHash(defines_).ToString() + extension; if (!LoadByteCode(binaryShaderName)) { // Compile shader if don't have valid bytecode if (!Compile()) return false; // Save the bytecode after successful compile, but not if the source is from a package if (owner_->GetTimeStamp()) SaveByteCode(binaryShaderName); } // Then create shader from the bytecode IDirect3DDevice9* device = graphics_->GetImpl()->GetDevice(); if (type_ == VS) { HRESULT hr = device->CreateVertexShader( (const DWORD*)&byteCode_[0], (IDirect3DVertexShader9**)&object_.ptr_); if (FAILED(hr)) { ATOMIC_SAFE_RELEASE(object_.ptr_); compilerOutput_ = "Could not create vertex shader (HRESULT " + ToStringHex((unsigned)hr) + ")"; } } else { HRESULT hr = device->CreatePixelShader( (const DWORD*)&byteCode_[0], (IDirect3DPixelShader9**)&object_.ptr_); if (FAILED(hr)) { ATOMIC_SAFE_RELEASE(object_.ptr_); compilerOutput_ = "Could not create pixel shader (HRESULT " + ToStringHex((unsigned)hr) + ")"; } } // The bytecode is not needed on Direct3D9 after creation, so delete it to save memory byteCode_.Clear(); byteCode_.Reserve(0); return object_.ptr_ != 0; }
bool ShaderVariation::Compile() { const String& sourceCode = owner_->GetSourceCode(type_); Vector<String> defines = defines_.Split(' '); // Set the entrypoint, profile and flags according to the shader being compiled const char* entryPoint = 0; const char* profile = 0; unsigned flags = D3DCOMPILE_OPTIMIZATION_LEVEL3; if (type_ == VS) { entryPoint = "VS"; defines.Push("COMPILEVS"); profile = "vs_3_0"; } else { entryPoint = "PS"; defines.Push("COMPILEPS"); profile = "ps_3_0"; flags |= D3DCOMPILE_PREFER_FLOW_CONTROL; } defines.Push("MAXBONES=" + String(Graphics::GetMaxBones())); // Collect defines into macros Vector<String> defineValues; PODVector<D3D_SHADER_MACRO> macros; for (unsigned i = 0; i < defines.Size(); ++i) { unsigned equalsPos = defines[i].Find('='); if (equalsPos != String::NPOS) { defineValues.Push(defines[i].Substring(equalsPos + 1)); defines[i].Resize(equalsPos); } else defineValues.Push("1"); } for (unsigned i = 0; i < defines.Size(); ++i) { D3D_SHADER_MACRO macro; macro.Name = defines[i].CString(); macro.Definition = defineValues[i].CString(); macros.Push(macro); // In debug mode, check that all defines are referenced by the shader code #ifdef _DEBUG if (sourceCode.Find(defines[i]) == String::NPOS) ATOMIC_LOGWARNING("Shader " + GetFullName() + " does not use the define " + defines[i]); #endif } D3D_SHADER_MACRO endMacro; endMacro.Name = 0; endMacro.Definition = 0; macros.Push(endMacro); // Compile using D3DCompile ID3DBlob* shaderCode = 0; ID3DBlob* errorMsgs = 0; HRESULT hr = D3DCompile(sourceCode.CString(), sourceCode.Length(), owner_->GetName().CString(), ¯os.Front(), 0, entryPoint, profile, flags, 0, &shaderCode, &errorMsgs); if (FAILED(hr)) { // Do not include end zero unnecessarily compilerOutput_ = String((const char*)errorMsgs->GetBufferPointer(), (unsigned)errorMsgs->GetBufferSize() - 1); } else { if (type_ == VS) ATOMIC_LOGDEBUG("Compiled vertex shader " + GetFullName()); else ATOMIC_LOGDEBUG("Compiled pixel shader " + GetFullName()); // Inspect the produced bytecode using MojoShader, then strip and store it unsigned char* bufData = (unsigned char*)shaderCode->GetBufferPointer(); unsigned bufSize = (unsigned)shaderCode->GetBufferSize(); ParseParameters(bufData, bufSize); CopyStrippedCode(byteCode_, bufData, bufSize); } ATOMIC_SAFE_RELEASE(shaderCode); ATOMIC_SAFE_RELEASE(errorMsgs); return !byteCode_.Empty(); }
VertexDeclaration::VertexDeclaration(Graphics* graphics, ShaderVariation* vertexShader, VertexBuffer** vertexBuffers) : inputLayout_(0) { PODVector<D3D11_INPUT_ELEMENT_DESC> elementDescs; unsigned prevBufferDescs = 0; for (unsigned i = 0; i < MAX_VERTEX_STREAMS; ++i) { if (!vertexBuffers[i]) continue; const PODVector<VertexElement>& srcElements = vertexBuffers[i]->GetElements(); bool isExisting = false; for (unsigned j = 0; j < srcElements.Size(); ++j) { const VertexElement& srcElement = srcElements[j]; const char* semanticName = ShaderVariation::elementSemanticNames[srcElement.semantic_]; // Override existing element if necessary for (unsigned k = 0; k < prevBufferDescs; ++k) { if (elementDescs[k].SemanticName == semanticName && elementDescs[k].SemanticIndex == srcElement.index_) { isExisting = true; elementDescs[k].InputSlot = i; elementDescs[k].AlignedByteOffset = srcElement.offset_; elementDescs[k].InputSlotClass = srcElement.perInstance_ ? D3D11_INPUT_PER_INSTANCE_DATA : D3D11_INPUT_PER_VERTEX_DATA; elementDescs[k].InstanceDataStepRate = srcElement.perInstance_ ? 1 : 0; break; } } if (isExisting) continue; D3D11_INPUT_ELEMENT_DESC newDesc; newDesc.SemanticName = semanticName; newDesc.SemanticIndex = srcElement.index_; newDesc.Format = d3dElementFormats[srcElement.type_]; newDesc.InputSlot = (UINT)i; newDesc.AlignedByteOffset = srcElement.offset_; newDesc.InputSlotClass = srcElement.perInstance_ ? D3D11_INPUT_PER_INSTANCE_DATA : D3D11_INPUT_PER_VERTEX_DATA; newDesc.InstanceDataStepRate = srcElement.perInstance_ ? 1 : 0; elementDescs.Push(newDesc); } prevBufferDescs = elementDescs.Size(); } if (elementDescs.Empty()) return; const PODVector<unsigned char>& byteCode = vertexShader->GetByteCode(); HRESULT hr = graphics->GetImpl()->GetDevice()->CreateInputLayout(&elementDescs[0], (UINT)elementDescs.Size(), &byteCode[0], byteCode.Size(), (ID3D11InputLayout**)&inputLayout_); if (FAILED(hr)) { ATOMIC_SAFE_RELEASE(inputLayout_); ATOMIC_LOGERRORF("Failed to create input layout for shader %s due to missing vertex element(s) (HRESULT %x)", vertexShader->GetFullName().CString(), (unsigned)hr); } }
VertexDeclaration::~VertexDeclaration() { ATOMIC_SAFE_RELEASE(inputLayout_); }
bool Texture2D::Create() { Release(); if (!graphics_ || !width_ || !height_) return false; if (graphics_->IsDeviceLost()) { ATOMIC_LOGWARNING("Texture creation while device is lost"); return true; } unsigned pool = usage_ > TEXTURE_STATIC ? D3DPOOL_DEFAULT : D3DPOOL_MANAGED; unsigned d3dUsage = 0; switch (usage_) { case TEXTURE_DYNAMIC: d3dUsage |= D3DUSAGE_DYNAMIC; break; case TEXTURE_RENDERTARGET: d3dUsage |= D3DUSAGE_RENDERTARGET; break; case TEXTURE_DEPTHSTENCIL: d3dUsage |= D3DUSAGE_DEPTHSTENCIL; break; default: break; } IDirect3DDevice9* device = graphics_->GetImpl()->GetDevice(); // If creating a depth-stencil texture, and it is not supported, create a depth-stencil surface instead if (usage_ == TEXTURE_DEPTHSTENCIL && !graphics_->GetImpl()->CheckFormatSupport((D3DFORMAT)format_, d3dUsage, D3DRTYPE_TEXTURE)) { HRESULT hr = device->CreateDepthStencilSurface( (UINT)width_, (UINT)height_, (D3DFORMAT)format_, D3DMULTISAMPLE_NONE, 0, FALSE, (IDirect3DSurface9**)&renderSurface_->surface_, 0); if (FAILED(hr)) { ATOMIC_SAFE_RELEASE(renderSurface_->surface_); ATOMIC_LOGD3DERROR("Could not create depth-stencil surface", hr); return false; } levels_ = 1; } else { HRESULT hr = graphics_->GetImpl()->GetDevice()->CreateTexture( (UINT)width_, (UINT)height_, requestedLevels_, d3dUsage, (D3DFORMAT)format_, (D3DPOOL)pool, (IDirect3DTexture9**)&object_, 0); if (FAILED(hr)) { ATOMIC_SAFE_RELEASE(object_.ptr_); ATOMIC_LOGD3DERROR("Could not create texture", hr); return false; } levels_ = ((IDirect3DTexture9*)object_.ptr_)->GetLevelCount(); if (usage_ >= TEXTURE_RENDERTARGET) { hr = ((IDirect3DTexture9*)object_.ptr_)->GetSurfaceLevel(0, (IDirect3DSurface9**)&renderSurface_->surface_); if (FAILED(hr)) ATOMIC_LOGD3DERROR("Could not get rendertarget surface", hr); } } return true; }
bool Texture2D::GetData(unsigned level, void* dest) const { if (!object_.ptr_) { ATOMIC_LOGERROR("No texture created, can not get data"); return false; } if (!dest) { ATOMIC_LOGERROR("Null destination for getting data"); return false; } if (level >= levels_) { ATOMIC_LOGERROR("Illegal mip level for getting data"); return false; } if (graphics_->IsDeviceLost()) { ATOMIC_LOGWARNING("Getting texture data while device is lost"); return false; } int levelWidth = GetLevelWidth(level); int levelHeight = GetLevelHeight(level); D3DLOCKED_RECT d3dLockedRect; RECT d3dRect; d3dRect.left = 0; d3dRect.top = 0; d3dRect.right = levelWidth; d3dRect.bottom = levelHeight; IDirect3DSurface9* offscreenSurface = 0; // Need to use a offscreen surface & GetRenderTargetData() for rendertargets if (renderSurface_) { if (level != 0) { ATOMIC_LOGERROR("Can only get mip level 0 data from a rendertarget"); return false; } IDirect3DDevice9* device = graphics_->GetImpl()->GetDevice(); HRESULT hr = device->CreateOffscreenPlainSurface((UINT)width_, (UINT)height_, (D3DFORMAT)format_, D3DPOOL_SYSTEMMEM, &offscreenSurface, 0); if (FAILED(hr)) { ATOMIC_SAFE_RELEASE(offscreenSurface); ATOMIC_LOGD3DERROR("Could not create surface for getting rendertarget data", hr); return false; } hr = device->GetRenderTargetData((IDirect3DSurface9*)renderSurface_->GetSurface(), offscreenSurface); if (FAILED(hr)) { ATOMIC_LOGD3DERROR("Could not get rendertarget data", hr); offscreenSurface->Release(); return false; } hr = offscreenSurface->LockRect(&d3dLockedRect, &d3dRect, D3DLOCK_READONLY); if (FAILED(hr)) { ATOMIC_LOGD3DERROR("Could not lock surface for getting rendertarget data", hr); offscreenSurface->Release(); return false; } } else { HRESULT hr = ((IDirect3DTexture9*)object_.ptr_)->LockRect(level, &d3dLockedRect, &d3dRect, D3DLOCK_READONLY); if (FAILED(hr)) { ATOMIC_LOGD3DERROR("Could not lock texture", hr); return false; } } int height = levelHeight; if (IsCompressed()) height = (height + 3) >> 2; unsigned char* destPtr = (unsigned char*)dest; unsigned rowSize = GetRowDataSize(levelWidth); // GetRowDataSize() returns CPU-side (destination) data size, so need to convert for X8R8G8B8 if (format_ == D3DFMT_X8R8G8B8) rowSize = rowSize / 3 * 4; // Perform conversion to RGB / RGBA as necessary switch (format_) { default: for (int i = 0; i < height; ++i) { unsigned char* src = (unsigned char*)d3dLockedRect.pBits + i * d3dLockedRect.Pitch; memcpy(destPtr, src, rowSize); destPtr += rowSize; } break; case D3DFMT_X8R8G8B8: for (int i = 0; i < height; ++i) { unsigned char* src = (unsigned char*)d3dLockedRect.pBits + i * d3dLockedRect.Pitch; for (int j = 0; j < levelWidth; ++j) { destPtr[2] = *src++; destPtr[1] = *src++; destPtr[0] = *src++; ++src; destPtr += 3; } } break; case D3DFMT_A8R8G8B8: for (int i = 0; i < height; ++i) { unsigned char* src = (unsigned char*)d3dLockedRect.pBits + i * d3dLockedRect.Pitch; for (int j = 0; j < levelWidth; ++j) { destPtr[2] = *src++; destPtr[1] = *src++; destPtr[0] = *src++; destPtr[3] = *src++; destPtr += 4; } } break; } if (offscreenSurface) { offscreenSurface->UnlockRect(); offscreenSurface->Release(); } else ((IDirect3DTexture9*)object_.ptr_)->UnlockRect(level); return true; }