Skip to content

Commit

Permalink
Add support for getting texture pixel data from render target
Browse files Browse the repository at this point in the history
  • Loading branch information
mogemimi committed Sep 28, 2019
1 parent 46ae505 commit 62ccd80
Show file tree
Hide file tree
Showing 11 changed files with 240 additions and 8 deletions.
12 changes: 12 additions & 0 deletions include/Pomdog/Graphics/RenderTarget2D.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,18 @@ class POMDOG_EXPORT RenderTarget2D final : public Texture {
/// Gets the size of the texture resource.
Rectangle GetBounds() const;

/// Copies the pixel data from texture to memory.
template <typename T>
void GetData(T* result, std::size_t startIndex, std::size_t elementCount) const
{
static_assert(std::is_pod_v<T>, "You can only use plain-old-data types.");
static_assert(!std::is_void_v<T>);
this->GetData(static_cast<void*>(result), sizeof(T) * startIndex, sizeof(T) * elementCount);
}

/// Copies the pixel data from texture to memory.
void GetData(void* result, std::size_t offsetInBytes, std::size_t sizeInBytes) const;

/// Gets the pointer of the native render target.
Detail::NativeRenderTarget2D* GetNativeRenderTarget2D();

Expand Down
17 changes: 17 additions & 0 deletions src/Graphics/RenderTarget2D.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,23 @@ Rectangle RenderTarget2D::GetBounds() const
return Rectangle{0, 0, pixelWidth, pixelHeight};
}

void RenderTarget2D::GetData(void* result, std::size_t offsetInBytes, std::size_t sizeInBytes) const
{
POMDOG_ASSERT(offsetInBytes >= 0);
POMDOG_ASSERT(sizeInBytes > 0);
POMDOG_ASSERT(result != nullptr);
POMDOG_ASSERT(nativeRenderTarget2D);

nativeRenderTarget2D->GetData(
result,
offsetInBytes,
sizeInBytes,
pixelWidth,
pixelHeight,
levelCount,
format);
}

Detail::NativeRenderTarget2D* RenderTarget2D::GetNativeRenderTarget2D()
{
return nativeRenderTarget2D.get();
Expand Down
93 changes: 85 additions & 8 deletions src/RenderSystem.Direct3D11/RenderTarget2DDirect3D11.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ void BuildRenderTarget(
std::int32_t pixelWidth,
std::int32_t pixelHeight,
std::int32_t levelCount,
bool isSharedTexture,
ComPtr<ID3D11Texture2D> & texture2D,
ComPtr<ID3D11RenderTargetView> & renderTargetView,
ComPtr<ID3D11ShaderResourceView> & textureResourceView)
Expand All @@ -40,10 +39,7 @@ void BuildRenderTarget(
textureDesc.Usage = D3D11_USAGE_DEFAULT;
textureDesc.BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE;
textureDesc.CPUAccessFlags = 0;
textureDesc.MiscFlags = (isSharedTexture ? D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX : 0);

POMDOG_ASSERT(isSharedTexture
? textureDesc.Format == DXGI_FORMAT_B8G8R8A8_UNORM : true);
textureDesc.MiscFlags = 0;

HRESULT hr = device->CreateTexture2D(&textureDesc, nullptr, &texture2D);

Expand Down Expand Up @@ -182,15 +178,15 @@ RenderTarget2DDirect3D11::RenderTarget2DDirect3D11(
std::int32_t levelCount,
SurfaceFormat format,
DepthFormat depthStencilFormat,
std::int32_t multiSampleCount)
[[maybe_unused]] std::int32_t multiSampleCount)
{
POMDOG_ASSERT(levelCount > 0);

///@todo MSAA is not implemented yet
UNREFERENCED_PARAMETER(multiSampleCount);

BuildRenderTarget(device, format, pixelWidth, pixelHeight, levelCount,
false, texture2D, renderTargetView, textureResourceView);
texture2D, renderTargetView, textureResourceView);

BuildDepthBuffer(device, depthStencilFormat, pixelWidth, pixelHeight,
levelCount, depthStencil, depthStencilView);
Expand All @@ -202,7 +198,7 @@ RenderTarget2DDirect3D11::RenderTarget2DDirect3D11(
std::int32_t pixelWidth,
std::int32_t pixelHeight,
DepthFormat depthStencilFormat,
std::int32_t multiSampleCount)
[[maybe_unused]] std::int32_t multiSampleCount)
{
///@todo MSAA is not implemented yet
UNREFERENCED_PARAMETER(multiSampleCount);
Expand All @@ -216,6 +212,87 @@ RenderTarget2DDirect3D11::RenderTarget2DDirect3D11(
backBufferMipLevels, depthStencil, depthStencilView);
}

void RenderTarget2DDirect3D11::GetData(
void* result,
std::size_t offsetInBytes,
std::size_t sizeInBytes,
[[maybe_unused]] std::int32_t pixelWidth,
[[maybe_unused]] std::int32_t pixelHeight,
[[maybe_unused]] std::int32_t levelCount,
[[maybe_unused]] SurfaceFormat format) const
{
POMDOG_ASSERT(texture2D);

// NOTE: Get the device context
ComPtr<ID3D11Device> device;
texture2D->GetDevice(&device);
ComPtr<ID3D11DeviceContext> deviceContext;
device->GetImmediateContext(&deviceContext);

POMDOG_ASSERT(deviceContext != nullptr);

// NOTE: Map the texture
D3D11_MAPPED_SUBRESOURCE mappedResource;
auto hr = deviceContext->Map(
texture2D.Get(),
0,
D3D11_MAP_READ,
0,
&mappedResource);

ComPtr<ID3D11Texture2D> mappedTexture;

if (!FAILED(hr)) {
mappedTexture = texture2D;
}
else if (hr == E_INVALIDARG) {
// NOTE: If we failed to map the texture, copy it to a staging resource.
D3D11_TEXTURE2D_DESC desc;
texture2D->GetDesc(&desc);

desc.Usage = D3D11_USAGE_STAGING;
desc.BindFlags = 0;
desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
desc.MiscFlags = 0;

ComPtr<ID3D11Texture2D> stagingTexture;
hr = device->CreateTexture2D(&desc, nullptr, &stagingTexture);
if (FAILED(hr)) {
// FUS RO DAH!
POMDOG_THROW_EXCEPTION(std::runtime_error, "Failed to create staging texture");
}

// NOTE: Copy the texture to a staging resource.
deviceContext->CopyResource(stagingTexture.Get(), texture2D.Get());

// NOTE: Map the staging resource
hr = deviceContext->Map(
stagingTexture.Get(),
0,
D3D11_MAP_READ,
0,
&mappedResource);

if (FAILED(hr)) {
// FUS RO DAH!
POMDOG_THROW_EXCEPTION(std::runtime_error, "Failed to map staging texture");
}

mappedTexture = std::move(stagingTexture);
}
else {
// FUS RO DAH!
POMDOG_THROW_EXCEPTION(std::runtime_error, "Failed to map buffer");
}

POMDOG_ASSERT(result != nullptr);
POMDOG_ASSERT(sizeInBytes > 0);
std::memcpy(result, reinterpret_cast<const BYTE*>(mappedResource.pData) + offsetInBytes, sizeInBytes);

POMDOG_ASSERT(mappedTexture != nullptr);
deviceContext->Unmap(mappedTexture.Get(), 0);
}

ID3D11RenderTargetView* RenderTarget2DDirect3D11::GetRenderTargetView() const
{
POMDOG_ASSERT(renderTargetView);
Expand Down
9 changes: 9 additions & 0 deletions src/RenderSystem.Direct3D11/RenderTarget2DDirect3D11.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,15 @@ class RenderTarget2DDirect3D11 final : public NativeRenderTarget2D {
DepthFormat depthStencilFormat,
std::int32_t multiSampleCount);

void GetData(
void* result,
std::size_t offsetInBytes,
std::size_t sizeInBytes,
std::int32_t pixelWidth,
std::int32_t pixelHeight,
std::int32_t levelCount,
SurfaceFormat format) const override;

ID3D11RenderTargetView* GetRenderTargetView() const;

ID3D11DepthStencilView* GetDepthStencilView() const;
Expand Down
19 changes: 19 additions & 0 deletions src/RenderSystem.GL4/RenderTarget2DGL4.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,25 @@ RenderTarget2DGL4::~RenderTarget2DGL4()
}
}

void RenderTarget2DGL4::GetData(
void* result,
std::size_t offsetInBytes,
std::size_t sizeInBytes,
std::int32_t pixelWidth,
std::int32_t pixelHeight,
std::int32_t levelCount,
SurfaceFormat format) const
{
texture.GetData(
result,
offsetInBytes,
sizeInBytes,
pixelWidth,
pixelHeight,
levelCount,
format);
}

void RenderTarget2DGL4::BindToFramebuffer(GLenum attachmentPoint)
{
GLenum textureTarget = (multiSampleEnabled
Expand Down
9 changes: 9 additions & 0 deletions src/RenderSystem.GL4/RenderTarget2DGL4.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,15 @@ class RenderTarget2DGL4 final : public NativeRenderTarget2D {

~RenderTarget2DGL4();

void GetData(
void* result,
std::size_t offsetInBytes,
std::size_t sizeInBytes,
std::int32_t pixelWidth,
std::int32_t pixelHeight,
std::int32_t levelCount,
SurfaceFormat format) const override;

void BindToFramebuffer(GLenum attachmentPoint);
void UnbindFromFramebuffer(GLenum attachmentPoint);

Expand Down
34 changes: 34 additions & 0 deletions src/RenderSystem.GL4/Texture2DGL4.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,40 @@ Texture2DGL4::~Texture2DGL4()
}
}

void Texture2DGL4::GetData(
void* result,
[[maybe_unused]] std::size_t offsetInBytes,
std::size_t sizeInBytes,
std::int32_t pixelWidth,
std::int32_t pixelHeight,
[[maybe_unused]] std::int32_t levelCount,
SurfaceFormat format) const
{
const auto oldTexture = TypesafeHelperGL4::Get<Texture2DObjectGL4>();
ScopeGuard scope([&] { TypesafeHelperGL4::BindTexture(oldTexture); });

POMDOG_ASSERT(textureObject);
TypesafeHelperGL4::BindTexture(*textureObject);
POMDOG_CHECK_ERROR_GL4("glBindTexture");

auto const formatComponents = ToFormatComponents(format);
auto const pixelFundamentalType = ToPixelFundamentalType(format);
auto const bytesPerBlock = SurfaceFormatHelper::ToBytesPerBlock(format);

// FIXME: Not implemented yet.
POMDOG_ASSERT(sizeInBytes >= static_cast<std::size_t>(bytesPerBlock * pixelWidth * pixelHeight));
if (sizeInBytes < static_cast<std::size_t>(bytesPerBlock * pixelWidth * pixelHeight)) {
return;
}

glGetTexImage(
GL_TEXTURE_2D,
0,
formatComponents,
pixelFundamentalType,
result);
}

void Texture2DGL4::SetData(std::int32_t pixelWidth, std::int32_t pixelHeight,
std::int32_t levelCount, SurfaceFormat format, const void* pixelData)
{
Expand Down
9 changes: 9 additions & 0 deletions src/RenderSystem.GL4/Texture2DGL4.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,15 @@ class Texture2DGL4 final : public NativeTexture2D {

~Texture2DGL4() override;

void GetData(
void* result,
std::size_t offsetInBytes,
std::size_t sizeInBytes,
std::int32_t pixelWidth,
std::int32_t pixelHeight,
std::int32_t levelCount,
SurfaceFormat format) const;

void SetData(
std::int32_t pixelWidth,
std::int32_t pixelHeight,
Expand Down
9 changes: 9 additions & 0 deletions src/RenderSystem.Metal/RenderTarget2DMetal.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,15 @@ class RenderTarget2DMetal final : public NativeRenderTarget2D {
DepthFormat depthStencilFormat,
std::int32_t multiSampleCount);

void GetData(
void* result,
std::size_t offsetInBytes,
std::size_t sizeInBytes,
std::int32_t pixelWidth,
std::int32_t pixelHeight,
std::int32_t levelCount,
SurfaceFormat format) const override;

id<MTLTexture> GetTexture() const noexcept;

id<MTLTexture> GetDepthStencilTexture() const noexcept;
Expand Down
24 changes: 24 additions & 0 deletions src/RenderSystem.Metal/RenderTarget2DMetal.mm
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include "RenderTarget2DMetal.hpp"
#include "MetalFormatHelper.hpp"
#include "../RenderSystem/SurfaceFormatHelper.hpp"
#include "Pomdog/Graphics/DepthFormat.hpp"
#include "Pomdog/Logging/Log.hpp"
#include "Pomdog/Utility/Assert.hpp"
Expand Down Expand Up @@ -74,6 +75,29 @@
}
}

void RenderTarget2DMetal::GetData(
void* result,
std::size_t offsetInBytes,
std::size_t sizeInBytes,
std::int32_t pixelWidth,
std::int32_t pixelHeight,
[[maybe_unused]] std::int32_t levelCount,
SurfaceFormat format) const
{
POMDOG_ASSERT(texture != nil);
POMDOG_ASSERT(result != nullptr);

auto const bytesPerPixel = SurfaceFormatHelper::ToBytesPerBlock(format);

// FIXME: Not implemented yet.
POMDOG_ASSERT(offsetInBytes == 0);
POMDOG_ASSERT(sizeInBytes == static_cast<std::size_t>(bytesPerPixel * pixelWidth * pixelHeight));
MTLRegion region = MTLRegionMake2D(0, 0, pixelWidth, pixelHeight);

// NOTE: Don't use getBytes() for textures with MTLResourceStorageModePrivate.
[texture getBytes:result bytesPerRow:(bytesPerPixel * pixelWidth) fromRegion:region mipmapLevel:0];
}

id<MTLTexture> RenderTarget2DMetal::GetTexture() const noexcept
{
return texture;
Expand Down
13 changes: 13 additions & 0 deletions src/RenderSystem/NativeRenderTarget2D.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

#pragma once

#include "Pomdog/Graphics/detail/ForwardDeclarations.hpp"
#include <cstddef>
#include <cstdint>

namespace Pomdog::Detail {

class NativeRenderTarget2D {
Expand All @@ -11,6 +15,15 @@ class NativeRenderTarget2D {
NativeRenderTarget2D & operator=(const NativeRenderTarget2D&) = delete;

virtual ~NativeRenderTarget2D() = default;

virtual void GetData(
void* result,
std::size_t offsetInBytes,
std::size_t sizeInBytes,
std::int32_t pixelWidth,
std::int32_t pixelHeight,
std::int32_t levelCount,
SurfaceFormat format) const = 0;
};

} // namespace Pomdog::Detail

0 comments on commit 62ccd80

Please sign in to comment.