Background
I'm generating a memory texture at load time to store the offsets into a texture atlas for a tilemap as described here Any way to combine instantiated sprite renderers into one texture so I can apply into a plane at runtime? (but in DirectX, not Unity.)
I then read that texture at runtime in the geometry shader to generate a tile mesh from each tile. This works for static tiles. Animated tiles require that the tile indicies be updated in the texture every frame.
Problem
This is where I'm stuck. The data loaded into the texture is not the same when it is retrieved via a call to ID3D11DeviceContext::Map
. The stride, pitch, or even the format is different such that the texture does not get fully updated or the wrong pixels are generated.
Question
Given the implementation of UpdateTileIndexTexture
below, what am I doing wrong in order to fully overwrite the initial texture data with the correct pixel data?
Implementation
Initial generation of the dynamic texture. This step works.
void Map::GenerateTileIndexTexture() noexcept {
const auto dims = CalcMaxDimensions();
const auto width = static_cast<std::size_t>(dims.x);
const auto height = static_cast<std::size_t>(dims.y);
std::vector<Rgba> data(width * height, Rgba::Black);
auto data_iter = std::begin(data);
for(auto& layer : _layers) {
for(auto& tile : *layer) {
const auto* def = tile.GetDefinition();
const auto coords = def->GetSprite()->GetCurrentSpriteCoords();
data_iter->r = static_cast<unsigned char>(coords.x);
data_iter->g = static_cast<unsigned char>(coords.y);
++data_iter;
}
}
_tile_index_texture = _renderer.Create2DTextureFromMemory(data, static_cast<unsigned int>(width), static_cast<unsigned int>(height), BufferUsage::Dynamic);
auto* mat = GetTileMaterial();
mat->SetTextureSlot(Material::TextureID::Normal, _tile_index_texture.get());
}
Implementation of Create2DTextureFromMemory
for reference:
std::unique_ptr<Texture> Renderer::Create2DTextureFromMemory(const std::vector<Rgba>& data, unsigned int width /*= 1*/, unsigned int height /*= 1*/, const BufferUsage& bufferUsage /*= BufferUsage::STATIC*/, const BufferBindUsage& bindUsage /*= BufferBindUsage::SHADER_RESOURCE*/, const ImageFormat& imageFormat /*= ImageFormat::R8G8B8A8_UNORM*/) const noexcept {
D3D11_TEXTURE2D_DESC tex_desc{};
//...Snip
// Setup Initial Data
D3D11_SUBRESOURCE_DATA subresource_data = {};
subresource_data.pSysMem = data.data();
subresource_data.SysMemPitch = width * sizeof(Rgba);
subresource_data.SysMemSlicePitch = width * height * sizeof(Rgba);
Microsoft::WRL::ComPtr<ID3D11Texture2D> dx_tex{};
//If IMMUTABLE or not multi-sampled, must use initial data.
bool isMultiSampled = tex_desc.SampleDesc.Count != 1 || tex_desc.SampleDesc.Quality != 0;
bool isImmutable = bufferUsage == BufferUsage::Static;
bool mustUseInitialData = isImmutable || !isMultiSampled;
HRESULT hr = _rhi_device->GetDxDevice()->CreateTexture2D(&tex_desc, (mustUseInitialData ? &subresource_data : nullptr), &dx_tex);
bool succeeded = SUCCEEDED(hr);
if(succeeded) {
return std::make_unique<Texture2D>(*_rhi_device, dx_tex);
} else {
return nullptr;
}
}
Attempt at updating the dynamic texture at runtime:
void Map::UpdateTileIndexTexture() noexcept {
const auto dims = CalcMaxDimensions();
const auto width = static_cast<std::size_t>(dims.x);
const auto height = static_cast<std::size_t>(dims.y);
std::vector<Rgba> data(width * height, Rgba::Black);
auto data_iter = std::begin(data);
for(auto& layer : _layers) {
for(auto& tile : *layer) {
if(const auto* def = tile.GetDefinition(); def != nullptr) {
const auto spriteCoords = Vector2{def->GetSprite()->GetCurrentSpriteCoords()};
data_iter->r = static_cast<unsigned char>(spriteCoords.x);
data_iter->g = static_cast<unsigned char>(spriteCoords.y);
++data_iter;
}
}
}
D3D11_MAPPED_SUBRESOURCE resource{};
auto* dx_context = _renderer.GetDeviceContext()->GetDxContext();
auto* dx_resource = _tile_index_texture->GetDxResource();
if(HRESULT hr = dx_context->Map(dx_resource, 0, D3D11_MAP_WRITE_DISCARD, 0U, &resource); SUCCEEDED(hr)) {
std::memcpy(resource.pData, data.data(), data.size() * sizeof(Rgba));
dx_context->Unmap(dx_resource, 0);
}
}
```