VideoBackends: Add AbstractStagingTexture class

Can be used for asynchronous readback or upload of textures.
This commit is contained in:
Stenzek
2017-10-22 00:49:40 +10:00
parent a584ccc7d8
commit f43d85921d
33 changed files with 1220 additions and 12 deletions

View File

@ -0,0 +1,133 @@
// Copyright 2017 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include <algorithm>
#include <cstring>
#include "Common/Assert.h"
#include "Common/MsgHandler.h"
#include "VideoCommon/AbstractStagingTexture.h"
#include "VideoCommon/AbstractTexture.h"
AbstractStagingTexture::AbstractStagingTexture(StagingTextureType type, const TextureConfig& c)
: m_type(type), m_config(c), m_texel_size(AbstractTexture::GetTexelSizeForFormat(c.format))
{
}
AbstractStagingTexture::~AbstractStagingTexture() = default;
void AbstractStagingTexture::CopyFromTexture(const AbstractTexture* src, u32 src_layer,
u32 src_level)
{
MathUtil::Rectangle<int> src_rect = src->GetConfig().GetMipRect(src_level);
MathUtil::Rectangle<int> dst_rect = m_config.GetRect();
CopyFromTexture(src, src_rect, src_layer, src_level, dst_rect);
}
void AbstractStagingTexture::CopyToTexture(AbstractTexture* dst, u32 dst_layer, u32 dst_level)
{
MathUtil::Rectangle<int> src_rect = m_config.GetRect();
MathUtil::Rectangle<int> dst_rect = dst->GetConfig().GetMipRect(dst_level);
CopyToTexture(src_rect, dst, dst_rect, dst_layer, dst_level);
}
void AbstractStagingTexture::ReadTexels(const MathUtil::Rectangle<int>& rect, void* out_ptr,
u32 out_stride)
{
_assert_(m_type != StagingTextureType::Upload);
if (!PrepareForAccess())
return;
_assert_(rect.left >= 0 && static_cast<u32>(rect.right) <= m_config.width && rect.top >= 0 &&
static_cast<u32>(rect.bottom) <= m_config.height);
// Offset pointer to point to start of region being copied out.
const char* current_ptr = m_map_pointer;
current_ptr += rect.top * m_map_stride;
current_ptr += rect.left * m_texel_size;
// Optimal path: same dimensions, same stride.
if (rect.left == 0 && static_cast<u32>(rect.right) == m_config.width &&
m_map_stride == out_stride)
{
std::memcpy(out_ptr, current_ptr, m_map_stride * rect.GetHeight());
return;
}
size_t copy_size = std::min(static_cast<size_t>(rect.GetWidth() * m_texel_size), m_map_stride);
int copy_height = rect.GetHeight();
char* dst_ptr = reinterpret_cast<char*>(out_ptr);
for (int row = 0; row < copy_height; row++)
{
std::memcpy(dst_ptr, current_ptr, copy_size);
current_ptr += m_map_stride;
dst_ptr += out_stride;
}
}
void AbstractStagingTexture::ReadTexel(u32 x, u32 y, void* out_ptr)
{
_assert_(m_type != StagingTextureType::Upload);
if (!PrepareForAccess())
return;
_assert_(x < m_config.width && y < m_config.height);
const char* src_ptr = m_map_pointer + y * m_map_stride + x * m_texel_size;
std::memcpy(out_ptr, src_ptr, m_texel_size);
}
void AbstractStagingTexture::WriteTexels(const MathUtil::Rectangle<int>& rect, const void* in_ptr,
u32 in_stride)
{
_assert_(m_type != StagingTextureType::Readback);
if (!PrepareForAccess())
return;
_assert_(rect.left >= 0 && static_cast<u32>(rect.right) <= m_config.width && rect.top >= 0 &&
static_cast<u32>(rect.bottom) <= m_config.height);
// Offset pointer to point to start of region being copied to.
char* current_ptr = m_map_pointer;
current_ptr += rect.top * m_map_stride;
current_ptr += rect.left * m_texel_size;
// Optimal path: same dimensions, same stride.
if (rect.left == 0 && static_cast<u32>(rect.right) == m_config.width && m_map_stride == in_stride)
{
std::memcpy(current_ptr, in_ptr, m_map_stride * rect.GetHeight());
return;
}
size_t copy_size = std::min(static_cast<size_t>(rect.GetWidth() * m_texel_size), m_map_stride);
int copy_height = rect.GetHeight();
const char* src_ptr = reinterpret_cast<const char*>(in_ptr);
for (int row = 0; row < copy_height; row++)
{
std::memcpy(current_ptr, src_ptr, copy_size);
current_ptr += m_map_stride;
src_ptr += in_stride;
}
}
void AbstractStagingTexture::WriteTexel(u32 x, u32 y, const void* in_ptr)
{
_assert_(m_type != StagingTextureType::Readback);
if (!PrepareForAccess())
return;
_assert_(x < m_config.width && y < m_config.height);
char* dest_ptr = m_map_pointer + y * m_map_stride + x * m_texel_size;
std::memcpy(dest_ptr, in_ptr, m_texel_size);
}
bool AbstractStagingTexture::PrepareForAccess()
{
if (m_needs_flush)
{
if (IsMapped())
Unmap();
Flush();
}
return IsMapped() || Map();
}

View File

@ -0,0 +1,86 @@
// Copyright 2017 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#pragma once
#include <cstddef>
#include <string>
#include "Common/CommonTypes.h"
#include "Common/MathUtil.h"
#include "VideoCommon/TextureConfig.h"
class AbstractTexture;
class AbstractStagingTexture
{
public:
explicit AbstractStagingTexture(StagingTextureType type, const TextureConfig& c);
virtual ~AbstractStagingTexture();
const TextureConfig& GetConfig() const { return m_config; }
StagingTextureType GetType() const { return m_type; }
size_t GetTexelSize() const { return m_texel_size; }
bool IsMapped() const { return m_map_pointer != nullptr; }
char* GetMappedPointer() const { return m_map_pointer; }
size_t GetMappedStride() const { return m_map_stride; }
// Copies from the GPU texture object to the staging texture, which can be mapped/read by the CPU.
// Both src_rect and dst_rect must be with within the bounds of the the specified textures.
virtual void CopyFromTexture(const AbstractTexture* src, const MathUtil::Rectangle<int>& src_rect,
u32 src_layer, u32 src_level,
const MathUtil::Rectangle<int>& dst_rect) = 0;
// Wrapper for copying a whole layer of a texture to a readback texture.
// Assumes that the level of src texture and this texture have the same dimensions.
void CopyFromTexture(const AbstractTexture* src, u32 src_layer = 0, u32 src_level = 0);
// Copies from this staging texture to a GPU texture.
// Both src_rect and dst_rect must be with within the bounds of the the specified textures.
virtual void CopyToTexture(const MathUtil::Rectangle<int>& src_rect, AbstractTexture* dst,
const MathUtil::Rectangle<int>& dst_rect, u32 dst_layer,
u32 dst_level) = 0;
// Wrapper for copying a whole layer of a texture to a readback texture.
// Assumes that the level of src texture and this texture have the same dimensions.
void CopyToTexture(AbstractTexture* dst, u32 dst_layer = 0, u32 dst_level = 0);
// Maps the texture into the CPU address space, enabling it to read the contents.
// The Map call may not perform synchronization. If the contents of the staging texture
// has been updated by a CopyFromTexture call, you must call Flush() first.
// If persistent mapping is supported in the backend, this may be a no-op.
virtual bool Map() = 0;
// Unmaps the CPU-readable copy of the texture. May be a no-op on backends which
// support persistent-mapped buffers.
virtual void Unmap() = 0;
// Flushes pending writes from the CPU to the GPU, and reads from the GPU to the CPU.
// This may cause a command buffer flush depending on if one has occurred between the last
// call to CopyFromTexture()/CopyToTexture() and the Flush() call.
virtual void Flush() = 0;
// Reads the specified rectangle from the staging texture to out_ptr, with the specified stride
// (length in bytes of each row). CopyFromTexture must be called first. The contents of any
// texels outside of the rectangle used for CopyFromTexture is undefined.
void ReadTexels(const MathUtil::Rectangle<int>& rect, void* out_ptr, u32 out_stride);
void ReadTexel(u32 x, u32 y, void* out_ptr);
// Copies the texels from in_ptr to the staging texture, which can be read by the GPU, with the
// specified stride (length in bytes of each row). After updating the staging texture with all
// changes, call CopyToTexture() to update the GPU copy.
void WriteTexels(const MathUtil::Rectangle<int>& rect, const void* in_ptr, u32 in_stride);
void WriteTexel(u32 x, u32 y, const void* in_ptr);
protected:
bool PrepareForAccess();
const StagingTextureType m_type;
const TextureConfig m_config;
const size_t m_texel_size;
char* m_map_pointer = nullptr;
size_t m_map_stride = 0;
bool m_needs_flush = false;
};

View File

@ -100,7 +100,7 @@ AbstractTexture::MapRegionImpl(u32 level, u32 x, u32 y, u32 width, u32 height)
return {};
}
bool AbstractTexture::IsCompressedHostTextureFormat(AbstractTextureFormat format)
bool AbstractTexture::IsCompressedFormat(AbstractTextureFormat format)
{
switch (format)
{
@ -115,7 +115,7 @@ bool AbstractTexture::IsCompressedHostTextureFormat(AbstractTextureFormat format
}
}
size_t AbstractTexture::CalculateHostTextureLevelPitch(AbstractTextureFormat format, u32 row_length)
size_t AbstractTexture::CalculateStrideForFormat(AbstractTextureFormat format, u32 row_length)
{
switch (format)
{
@ -134,6 +134,25 @@ size_t AbstractTexture::CalculateHostTextureLevelPitch(AbstractTextureFormat for
}
}
size_t AbstractTexture::GetTexelSizeForFormat(AbstractTextureFormat format)
{
switch (format)
{
case AbstractTextureFormat::DXT1:
return 8;
case AbstractTextureFormat::DXT3:
case AbstractTextureFormat::DXT5:
case AbstractTextureFormat::BPTC:
return 16;
case AbstractTextureFormat::RGBA8:
case AbstractTextureFormat::BGRA8:
return 4;
default:
PanicAlert("Unhandled texture format.");
return 0;
}
}
const TextureConfig& AbstractTexture::GetConfig() const
{
return m_config;

View File

@ -39,8 +39,9 @@ public:
virtual void Load(u32 level, u32 width, u32 height, u32 row_length, const u8* buffer,
size_t buffer_size) = 0;
static bool IsCompressedHostTextureFormat(AbstractTextureFormat format);
static size_t CalculateHostTextureLevelPitch(AbstractTextureFormat format, u32 row_length);
static bool IsCompressedFormat(AbstractTextureFormat format);
static size_t CalculateStrideForFormat(AbstractTextureFormat format, u32 row_length);
static size_t GetTexelSizeForFormat(AbstractTextureFormat format);
const TextureConfig& GetConfig() const;

View File

@ -1,4 +1,5 @@
set(SRCS
AbstractStagingTexture.cpp
AbstractTexture.cpp
AsyncRequests.cpp
AsyncShaderCompiler.cpp

View File

@ -34,9 +34,11 @@
class AbstractRawTexture;
class AbstractTexture;
class AbstractStagingTexture;
class PostProcessingShaderImplementation;
struct TextureConfig;
enum class EFBAccessType;
enum class StagingTextureType;
struct EfbPokeData
{
@ -81,6 +83,8 @@ public:
virtual void ResetAPIState() {}
virtual void RestoreAPIState() {}
virtual std::unique_ptr<AbstractTexture> CreateTexture(const TextureConfig& config) = 0;
virtual std::unique_ptr<AbstractStagingTexture>
CreateStagingTexture(StagingTextureType type, const TextureConfig& config) = 0;
// Ideal internal resolution - multiple of the native EFB resolution
int GetTargetWidth() const { return m_target_width; }

View File

@ -3,6 +3,7 @@
// Refer to the license.txt file included.
#include "VideoCommon/TextureConfig.h"
#include "VideoCommon/AbstractTexture.h"
#include <tuple>
@ -12,7 +13,28 @@ bool TextureConfig::operator==(const TextureConfig& o) const
std::tie(o.width, o.height, o.levels, o.layers, o.format, o.rendertarget);
}
bool TextureConfig::operator!=(const TextureConfig& o) const
{
return !operator==(o);
}
MathUtil::Rectangle<int> TextureConfig::GetRect() const
{
return {0, 0, static_cast<int>(width), static_cast<int>(height)};
}
MathUtil::Rectangle<int> TextureConfig::GetMipRect(u32 level) const
{
return {0, 0, static_cast<int>(std::max(width >> level, 1u)),
static_cast<int>(std::max(height >> level, 1u))};
}
size_t TextureConfig::GetStride() const
{
return AbstractTexture::CalculateStrideForFormat(format, width);
}
size_t TextureConfig::GetMipStride(u32 level) const
{
return AbstractTexture::CalculateStrideForFormat(format, std::max(width >> level, 1u));
}

View File

@ -21,6 +21,13 @@ enum class AbstractTextureFormat : u32
Undefined
};
enum class StagingTextureType
{
Readback, // Optimize for CPU reads, GPU writes, no CPU writes
Upload, // Optimize for CPU writes, GPU reads, no CPU reads
Mutable // Optimize for CPU reads, GPU writes, allow slow CPU reads
};
struct TextureConfig
{
constexpr TextureConfig() = default;
@ -32,7 +39,11 @@ struct TextureConfig
}
bool operator==(const TextureConfig& o) const;
bool operator!=(const TextureConfig& o) const;
MathUtil::Rectangle<int> GetRect() const;
MathUtil::Rectangle<int> GetMipRect(u32 level) const;
size_t GetStride() const;
size_t GetMipStride(u32 level) const;
u32 width = 0;
u32 height = 0;

View File

@ -36,6 +36,7 @@
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<ItemGroup>
<ClCompile Include="AbstractStagingTexture.cpp" />
<ClCompile Include="AbstractTexture.cpp" />
<ClCompile Include="AsyncRequests.cpp" />
<ClCompile Include="AsyncShaderCompiler.cpp" />
@ -96,6 +97,7 @@
<ClCompile Include="XFStructs.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="AbstractStagingTexture.h" />
<ClInclude Include="AbstractTexture.h" />
<ClInclude Include="AsyncRequests.h" />
<ClInclude Include="AsyncShaderCompiler.h" />

View File

@ -191,6 +191,9 @@
<ClCompile Include="UberShaderVertex.cpp">
<Filter>Shader Generators</Filter>
</ClCompile>
<ClCompile Include="AbstractStagingTexture.cpp">
<Filter>Base</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="CommandProcessor.h" />
@ -362,6 +365,9 @@
<ClInclude Include="UberShaderVertex.h">
<Filter>Shader Generators</Filter>
</ClInclude>
<ClInclude Include="AbstractStagingTexture.h">
<Filter>Base</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Text Include="CMakeLists.txt" />