Share the conversion code between vulkan and D3D, fixing the missing format thing.

This commit is contained in:
Henrik Rydgård 2017-10-29 14:42:51 +01:00
parent 93c785b76d
commit 97dced543e
12 changed files with 142 additions and 180 deletions

View file

@ -808,7 +808,8 @@ if(VULKAN)
ext/native/thin3d/VulkanRenderManager.cpp
ext/native/thin3d/VulkanRenderManager.h
ext/native/thin3d/VulkanQueueRunner.cpp
ext/native/thin3d/VulkanQueueRunner.h)
ext/native/thin3d/VulkanQueueRunner.h
ext/native/thin3d/DataFormat.h)
endif()
if(WIN32)
set(THIN3D_PLATFORMS ${THIN3D_PLATFORMS}

View file

@ -240,6 +240,7 @@
<ClInclude Include="gfx_es2\draw_text_qt.h" />
<ClInclude Include="gfx_es2\draw_text_win.h" />
<ClInclude Include="thin3d\d3d11_loader.h" />
<ClInclude Include="thin3d\DataFormat.h" />
<ClInclude Include="thin3d\VulkanQueueRunner.h" />
<ClInclude Include="thin3d\VulkanRenderManager.h" />
<ClInclude Include="util\text\wrap_text.h" />
@ -760,4 +761,4 @@
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
</Project>

View file

@ -326,6 +326,9 @@
<ClInclude Include="thin3d\VulkanRenderManager.h">
<Filter>thin3d</Filter>
</ClInclude>
<ClInclude Include="thin3d\DataFormat.h">
<Filter>thin3d</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="gfx\gl_debug_log.cpp">
@ -865,4 +868,4 @@
<UniqueIdentifier>{06c6305a-a646-485b-85b9-645a24dd6553}</UniqueIdentifier>
</Filter>
</ItemGroup>
</Project>
</Project>

View file

@ -0,0 +1,71 @@
#pragma once
#include <cstdint>
namespace Draw {
enum class DataFormat : uint8_t {
UNDEFINED,
R8_UNORM,
R8G8_UNORM,
R8G8B8_UNORM,
R8G8B8A8_UNORM,
R8G8B8A8_UNORM_SRGB,
B8G8R8A8_UNORM, // D3D style
B8G8R8A8_UNORM_SRGB, // D3D style
R8G8B8A8_SNORM,
R8G8B8A8_UINT,
R8G8B8A8_SINT,
R4G4_UNORM_PACK8,
A4R4G4B4_UNORM_PACK16, // A4 in the UPPER bit
B4G4R4A4_UNORM_PACK16,
R4G4B4A4_UNORM_PACK16,
R5G6B5_UNORM_PACK16,
B5G6R5_UNORM_PACK16,
R5G5B5A1_UNORM_PACK16, // A1 in the LOWER bit
B5G5R5A1_UNORM_PACK16, // A1 in the LOWER bit
A1R5G5B5_UNORM_PACK16, // A1 in the UPPER bit.
R16_FLOAT,
R16G16_FLOAT,
R16G16B16A16_FLOAT,
R32_FLOAT,
R32G32_FLOAT,
R32G32B32_FLOAT,
R32G32B32A32_FLOAT,
// Block compression formats.
// These are modern names for DXT and friends, now patent free.
// https://msdn.microsoft.com/en-us/library/bb694531.aspx
BC1_RGBA_UNORM_BLOCK,
BC1_RGBA_SRGB_BLOCK,
BC2_UNORM_BLOCK, // 4-bit straight alpha + DXT1 color. Usually not worth using
BC2_SRGB_BLOCK,
BC3_UNORM_BLOCK, // 3-bit alpha with 2 ref values (+ magic) + DXT1 color
BC3_SRGB_BLOCK,
BC4_UNORM_BLOCK, // 1-channel, same storage as BC3 alpha
BC4_SNORM_BLOCK,
BC5_UNORM_BLOCK, // 2-channel RG, each has same storage as BC3 alpha
BC5_SNORM_BLOCK,
BC6H_UFLOAT_BLOCK, // TODO
BC6H_SFLOAT_BLOCK,
BC7_UNORM_BLOCK, // Highly advanced, very expensive to compress, very good quality.
BC7_SRGB_BLOCK,
ETC1,
S8,
D16,
D24_S8,
D32F,
D32F_S8,
};
void ConvertFromRGBA8888(uint8_t *dst, const uint8_t *src, uint32_t dstStride, uint32_t srcStride, uint32_t width, uint32_t height, DataFormat format);
} // namespace

View file

@ -1,5 +1,4 @@
#include "Common/ColorConv.h"
#include "DataFormat.h"
#include "VulkanQueueRunner.h"
#include "VulkanRenderManager.h"
@ -747,60 +746,7 @@ void VulkanQueueRunner::PerformReadback(const VKRStep &step, VkCommandBuffer cmd
// NOTE: Can't read the buffer using the CPU here - need to sync first.
}
// Would love to share this with D3D11 but don't have a shared format enum we can use... as we don't want
// to depend on thin3d here.
// TODO: SSE/NEON
// Could also make C fake-simd for 64-bit, two 8888 pixels fit in a register :)
// Strides are in pixels.
void ConvertFromRGBA8888(u8 *dst, const u8 *src, u32 dstStride, u32 srcStride, u32 width, u32 height, VkFormat format) {
// Must skip stride in the cases below. Some games pack data into the cracks, like MotoGP.
const u32 *src32 = (const u32 *)src;
if (format == VK_FORMAT_R8G8B8A8_UNORM) {
u32 *dst32 = (u32 *)dst;
if (src == dst) {
return;
} else {
for (u32 y = 0; y < height; ++y) {
memcpy(dst32, src32, width * 4);
src32 += srcStride;
dst32 += dstStride;
}
}
} else {
// But here it shouldn't matter if they do intersect
u16 *dst16 = (u16 *)dst;
switch (format) {
case VK_FORMAT_B5G6R5_UNORM_PACK16: // BGR 565
for (u32 y = 0; y < height; ++y) {
ConvertRGBA8888ToRGB565(dst16, src32, width);
src32 += srcStride;
dst16 += dstStride;
}
break;
case VK_FORMAT_A1R5G5B5_UNORM_PACK16: // ABGR 1555
for (u32 y = 0; y < height; ++y) {
ConvertRGBA8888ToRGBA5551(dst16, src32, width);
src32 += srcStride;
dst16 += dstStride;
}
break;
case VK_FORMAT_R4G4B4A4_UNORM_PACK16: // ABGR 4444
for (u32 y = 0; y < height; ++y) {
ConvertRGBA8888ToRGBA4444(dst16, src32, width);
src32 += srcStride;
dst16 += dstStride;
}
break;
default:
Crash();
// Not possible.
break;
}
}
}
void VulkanQueueRunner::CopyReadbackBuffer(int width, int height, VkFormat destFormat, int pixelStride, uint8_t *pixels) {
void VulkanQueueRunner::CopyReadbackBuffer(int width, int height, Draw::DataFormat destFormat, int pixelStride, uint8_t *pixels) {
// Read back to the requested address in ram from buffer.
void *mappedData;
const int srcPixelSize = 4; // TODO: Fix.

View file

@ -4,7 +4,7 @@
#include "Common/Vulkan/VulkanContext.h"
#include "math/dataconv.h"
#include "thin3d/thin3d.h"
#include "thin3d/DataFormat.h"
class VKRFramebuffer;
struct VKRImage;
@ -156,7 +156,7 @@ public:
return (int)depth * 3 + (int)color;
}
void CopyReadbackBuffer(int width, int height, VkFormat destFormat, int pixelStride, uint8_t *pixels);
void CopyReadbackBuffer(int width, int height, Draw::DataFormat destFormat, int pixelStride, uint8_t *pixels);
private:
void InitBackbufferRenderPass();

View file

@ -327,7 +327,7 @@ void VulkanRenderManager::BindFramebufferAsRenderTarget(VKRFramebuffer *fb, VKRR
curHeight_ = fb ? fb->height : vulkan_->GetBackbufferHeight();
}
void VulkanRenderManager::CopyFramebufferToMemorySync(VKRFramebuffer *src, int aspectBits, int x, int y, int w, int h, VkFormat destFormat, uint8_t *pixels, int pixelStride) {
void VulkanRenderManager::CopyFramebufferToMemorySync(VKRFramebuffer *src, int aspectBits, int x, int y, int w, int h, Draw::DataFormat destFormat, uint8_t *pixels, int pixelStride) {
VKRStep *step = new VKRStep{ VKRStepType::READBACK };
step->readback.aspectMask = aspectBits;
step->readback.src = src;

View file

@ -11,7 +11,7 @@
#include "Common/Vulkan/VulkanContext.h"
#include "math/dataconv.h"
#include "thin3d/thin3d.h"
#include "thin3d/DataFormat.h"
#include "thin3d/VulkanQueueRunner.h"
// Simple independent framebuffer image. Gets its own allocation, we don't have that many framebuffers so it's fine
@ -85,7 +85,7 @@ public:
void BindFramebufferAsRenderTarget(VKRFramebuffer *fb, VKRRenderPassAction color, VKRRenderPassAction depth, uint32_t clearColor, float clearDepth, uint8_t clearStencil);
VkImageView BindFramebufferAsTexture(VKRFramebuffer *fb, int binding, int aspectBit, int attachment);
void CopyFramebufferToMemorySync(VKRFramebuffer *src, int aspectBits, int x, int y, int w, int h, VkFormat destFormat, uint8_t *pixels, int pixelStride);
void CopyFramebufferToMemorySync(VKRFramebuffer *src, int aspectBits, int x, int y, int w, int h, Draw::DataFormat destFormat, uint8_t *pixels, int pixelStride);
void CopyFramebuffer(VKRFramebuffer *src, VkRect2D srcRect, VKRFramebuffer *dst, VkOffset2D dstPos, int aspectMask);
void BlitFramebuffer(VKRFramebuffer *src, VkRect2D srcRect, VKRFramebuffer *dst, VkRect2D dstRect, int aspectMask, VkFilter filter);

View file

@ -1,7 +1,9 @@
#include <cstring>
#include <thin3d/thin3d.h>
#include <cstdint>
#include "base/logging.h"
#include "thin3d/thin3d.h"
#include "Common/ColorConv.h"
namespace Draw {
@ -320,4 +322,55 @@ DrawContext::~DrawContext() {
}
}
// TODO: SSE/NEON
// Could also make C fake-simd for 64-bit, two 8888 pixels fit in a register :)
void ConvertFromRGBA8888(uint8_t *dst, const uint8_t *src, uint32_t dstStride, uint32_t srcStride, uint32_t width, uint32_t height, DataFormat format) {
// Must skip stride in the cases below. Some games pack data into the cracks, like MotoGP.
const uint32_t *src32 = (const uint32_t *)src;
if (format == Draw::DataFormat::R8G8B8A8_UNORM) {
uint32_t *dst32 = (uint32_t *)dst;
if (src == dst) {
return;
} else {
for (uint32_t y = 0; y < height; ++y) {
memcpy(dst32, src32, width * 4);
src32 += srcStride;
dst32 += dstStride;
}
}
} else {
// But here it shouldn't matter if they do intersect
uint16_t *dst16 = (uint16_t *)dst;
switch (format) {
case Draw::DataFormat::R5G6B5_UNORM_PACK16: // BGR 565
for (uint32_t y = 0; y < height; ++y) {
ConvertRGBA8888ToRGB565(dst16, src32, width);
src32 += srcStride;
dst16 += dstStride;
}
break;
case Draw::DataFormat::A1R5G5B5_UNORM_PACK16: // ABGR 1555
for (uint32_t y = 0; y < height; ++y) {
ConvertRGBA8888ToRGBA5551(dst16, src32, width);
src32 += srcStride;
dst16 += dstStride;
}
break;
case Draw::DataFormat::A4R4G4B4_UNORM_PACK16: // ABGR 4444
for (uint32_t y = 0; y < height; ++y) {
ConvertRGBA8888ToRGBA4444(dst16, src32, width);
src32 += srcStride;
dst16 += dstStride;
}
break;
case Draw::DataFormat::R8G8B8A8_UNORM:
case Draw::DataFormat::UNDEFINED:
// Not possible.
break;
}
}
}
} // namespace Draw

View file

@ -12,6 +12,7 @@
#include <string>
#include "base/logging.h"
#include "DataFormat.h"
class Matrix4x4;
@ -176,68 +177,6 @@ enum class TextureType : uint8_t {
ARRAY2D,
};
enum class DataFormat : uint8_t {
UNDEFINED,
R8_UNORM,
R8G8_UNORM,
R8G8B8_UNORM,
R8G8B8A8_UNORM,
R8G8B8A8_UNORM_SRGB,
B8G8R8A8_UNORM, // D3D style
B8G8R8A8_UNORM_SRGB, // D3D style
R8G8B8A8_SNORM,
R8G8B8A8_UINT,
R8G8B8A8_SINT,
R4G4_UNORM_PACK8,
A4R4G4B4_UNORM_PACK16, // A4 in the UPPER bit
B4G4R4A4_UNORM_PACK16,
R4G4B4A4_UNORM_PACK16,
R5G6B5_UNORM_PACK16,
B5G6R5_UNORM_PACK16,
R5G5B5A1_UNORM_PACK16, // A1 in the LOWER bit
B5G5R5A1_UNORM_PACK16, // A1 in the LOWER bit
A1R5G5B5_UNORM_PACK16, // A1 in the UPPER bit.
R16_FLOAT,
R16G16_FLOAT,
R16G16B16A16_FLOAT,
R32_FLOAT,
R32G32_FLOAT,
R32G32B32_FLOAT,
R32G32B32A32_FLOAT,
// Block compression formats.
// These are modern names for DXT and friends, now patent free.
// https://msdn.microsoft.com/en-us/library/bb694531.aspx
BC1_RGBA_UNORM_BLOCK,
BC1_RGBA_SRGB_BLOCK,
BC2_UNORM_BLOCK, // 4-bit straight alpha + DXT1 color. Usually not worth using
BC2_SRGB_BLOCK,
BC3_UNORM_BLOCK, // 3-bit alpha with 2 ref values (+ magic) + DXT1 color
BC3_SRGB_BLOCK,
BC4_UNORM_BLOCK, // 1-channel, same storage as BC3 alpha
BC4_SNORM_BLOCK,
BC5_UNORM_BLOCK, // 2-channel RG, each has same storage as BC3 alpha
BC5_SNORM_BLOCK,
BC6H_UFLOAT_BLOCK, // TODO
BC6H_SFLOAT_BLOCK,
BC7_UNORM_BLOCK, // Highly advanced, very expensive to compress, very good quality.
BC7_SRGB_BLOCK,
ETC1,
S8,
D16,
D24_S8,
D32F,
D32F_S8,
};
enum class ShaderStage {
VERTEX,
FRAGMENT,

View file

@ -1310,56 +1310,6 @@ bool D3D11DrawContext::BlitFramebuffer(Framebuffer *srcfb, int srcX1, int srcY1,
return false;
}
// TODO: SSE/NEON
// Could also make C fake-simd for 64-bit, two 8888 pixels fit in a register :)
void ConvertFromRGBA8888(u8 *dst, u8 *src, u32 dstStride, u32 srcStride, u32 width, u32 height, Draw::DataFormat format) {
// Must skip stride in the cases below. Some games pack data into the cracks, like MotoGP.
const u32 *src32 = (const u32 *)src;
if (format == Draw::DataFormat::R8G8B8A8_UNORM) {
u32 *dst32 = (u32 *)dst;
if (src == dst) {
return;
} else {
for (u32 y = 0; y < height; ++y) {
memcpy(dst32, src32, width * 4);
src32 += srcStride;
dst32 += dstStride;
}
}
} else {
// But here it shouldn't matter if they do intersect
u16 *dst16 = (u16 *)dst;
switch (format) {
case Draw::DataFormat::R5G6B5_UNORM_PACK16: // BGR 565
for (u32 y = 0; y < height; ++y) {
ConvertRGBA8888ToRGB565(dst16, src32, width);
src32 += srcStride;
dst16 += dstStride;
}
break;
case Draw::DataFormat::A1R5G5B5_UNORM_PACK16: // ABGR 1555
for (u32 y = 0; y < height; ++y) {
ConvertRGBA8888ToRGBA5551(dst16, src32, width);
src32 += srcStride;
dst16 += dstStride;
}
break;
case Draw::DataFormat::A4R4G4B4_UNORM_PACK16: // ABGR 4444
for (u32 y = 0; y < height; ++y) {
ConvertRGBA8888ToRGBA4444(dst16, src32, width);
src32 += srcStride;
dst16 += dstStride;
}
break;
case Draw::DataFormat::R8G8B8A8_UNORM:
case Draw::DataFormat::UNDEFINED:
// Not possible.
break;
}
}
}
bool D3D11DrawContext::CopyFramebufferToMemorySync(Framebuffer *src, int channelBits, int bx, int by, int bw, int bh, Draw::DataFormat format, void *pixels, int pixelStride) {
D3D11Framebuffer *fb = (D3D11Framebuffer *)src;

View file

@ -1288,8 +1288,6 @@ bool VKContext::BlitFramebuffer(Framebuffer *srcfb, int srcX1, int srcY1, int sr
bool VKContext::CopyFramebufferToMemorySync(Framebuffer *srcfb, int channelBits, int x, int y, int w, int h, Draw::DataFormat format, void *pixels, int pixelStride) {
VKFramebuffer *src = (VKFramebuffer *)srcfb;
VkFormat vkFormat = DataFormatToVulkan(format);
int aspectMask = 0;
if (channelBits & FBChannel::FB_COLOR_BIT) {
aspectMask |= VK_IMAGE_ASPECT_COLOR_BIT;
@ -1297,7 +1295,7 @@ bool VKContext::CopyFramebufferToMemorySync(Framebuffer *srcfb, int channelBits,
if (channelBits & FBChannel::FB_DEPTH_BIT) aspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT;
if (channelBits & FBChannel::FB_STENCIL_BIT) aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT;
renderManager_.CopyFramebufferToMemorySync(src->GetFB(), aspectMask, x, y, w, h, vkFormat, (uint8_t *)pixels, pixelStride);
renderManager_.CopyFramebufferToMemorySync(src->GetFB(), aspectMask, x, y, w, h, format, (uint8_t *)pixels, pixelStride);
return true;
}