mirror of
https://github.com/RetroDECK/Duckstation.git
synced 2024-11-22 13:55:38 +00:00
GPU: Use Image class for screenshot saving
This commit is contained in:
parent
620d08f72d
commit
250b1bea8f
159
src/core/gpu.cpp
159
src/core/gpu.cpp
|
@ -1,4 +1,4 @@
|
||||||
// SPDX-FileCopyrightText: 2019-2023 Connor McLaughlin <stenzek@gmail.com>
|
// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com>
|
||||||
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
||||||
|
|
||||||
#include "gpu.h"
|
#include "gpu.h"
|
||||||
|
@ -12,6 +12,7 @@
|
||||||
#include "timers.h"
|
#include "timers.h"
|
||||||
|
|
||||||
#include "util/gpu_device.h"
|
#include "util/gpu_device.h"
|
||||||
|
#include "util/image.h"
|
||||||
#include "util/imgui_manager.h"
|
#include "util/imgui_manager.h"
|
||||||
#include "util/postprocessing.h"
|
#include "util/postprocessing.h"
|
||||||
#include "util/shadergen.h"
|
#include "util/shadergen.h"
|
||||||
|
@ -24,9 +25,6 @@
|
||||||
#include "common/small_string.h"
|
#include "common/small_string.h"
|
||||||
#include "common/string_util.h"
|
#include "common/string_util.h"
|
||||||
|
|
||||||
#include "stb_image_resize.h"
|
|
||||||
#include "stb_image_write.h"
|
|
||||||
|
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
|
@ -1906,9 +1904,8 @@ Common::Rectangle<s32> GPU::CalculateDrawRect(s32 window_width, s32 window_heigh
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool CompressAndWriteTextureToFile(u32 width, u32 height, std::string filename, FileSystem::ManagedCFilePtr fp,
|
static bool CompressAndWriteTextureToFile(u32 width, u32 height, std::string filename, FileSystem::ManagedCFilePtr fp,
|
||||||
u8 quality, bool clear_alpha, bool flip_y, u32 resize_width,
|
u8 quality, bool clear_alpha, bool flip_y, std::vector<u32> texture_data,
|
||||||
u32 resize_height, std::vector<u8> texture_data, u32 texture_data_stride,
|
u32 texture_data_stride, GPUTexture::Format texture_format)
|
||||||
GPUTexture::Format texture_format)
|
|
||||||
{
|
{
|
||||||
|
|
||||||
const char* extension = std::strrchr(filename.c_str(), '.');
|
const char* extension = std::strrchr(filename.c_str(), '.');
|
||||||
|
@ -1923,65 +1920,16 @@ static bool CompressAndWriteTextureToFile(u32 width, u32 height, std::string fil
|
||||||
|
|
||||||
if (clear_alpha)
|
if (clear_alpha)
|
||||||
{
|
{
|
||||||
for (u32 y = 0; y < height; y++)
|
for (u32& pixel : texture_data)
|
||||||
{
|
pixel |= 0xFF000000u;
|
||||||
u8* pixels = &texture_data[y * texture_data_stride];
|
|
||||||
for (u32 x = 0; x < width; x++)
|
|
||||||
{
|
|
||||||
u32 pixel;
|
|
||||||
std::memcpy(&pixel, pixels, sizeof(pixel));
|
|
||||||
pixel |= 0xFF000000u;
|
|
||||||
std::memcpy(pixels, &pixel, sizeof(pixel));
|
|
||||||
pixels += sizeof(pixel);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (flip_y)
|
if (flip_y)
|
||||||
GPUTexture::FlipTextureDataRGBA8(width, height, texture_data, texture_data_stride);
|
GPUTexture::FlipTextureDataRGBA8(width, height, reinterpret_cast<u8*>(texture_data.data()), texture_data_stride);
|
||||||
|
|
||||||
if (resize_width > 0 && resize_height > 0 && (resize_width != width || resize_height != height))
|
Assert(texture_data_stride == sizeof(u32) * width);
|
||||||
{
|
RGBA8Image image(width, height, std::move(texture_data));
|
||||||
std::vector<u8> resized_texture_data(resize_width * resize_height * sizeof(u32));
|
if (!image.SaveToFile(filename.c_str(), fp.get(), quality))
|
||||||
u32 resized_texture_stride = sizeof(u32) * resize_width;
|
|
||||||
if (!stbir_resize_uint8(texture_data.data(), width, height, texture_data_stride, resized_texture_data.data(),
|
|
||||||
resize_width, resize_height, resized_texture_stride, 4))
|
|
||||||
{
|
|
||||||
Log_ErrorPrintf("Failed to resize texture data from %ux%u to %ux%u", width, height, resize_width, resize_height);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
width = resize_width;
|
|
||||||
height = resize_height;
|
|
||||||
texture_data = std::move(resized_texture_data);
|
|
||||||
texture_data_stride = resized_texture_stride;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto write_func = [](void* context, void* data, int size) {
|
|
||||||
std::fwrite(data, 1, size, static_cast<std::FILE*>(context));
|
|
||||||
};
|
|
||||||
|
|
||||||
bool result = false;
|
|
||||||
if (StringUtil::Strcasecmp(extension, ".png") == 0)
|
|
||||||
{
|
|
||||||
// TODO: Use quality... libpng is better.
|
|
||||||
result =
|
|
||||||
(stbi_write_png_to_func(write_func, fp.get(), width, height, 4, texture_data.data(), texture_data_stride) != 0);
|
|
||||||
}
|
|
||||||
else if (StringUtil::Strcasecmp(extension, ".jpg") == 0)
|
|
||||||
{
|
|
||||||
result = (stbi_write_jpg_to_func(write_func, fp.get(), width, height, 4, texture_data.data(), quality) != 0);
|
|
||||||
}
|
|
||||||
else if (StringUtil::Strcasecmp(extension, ".tga") == 0)
|
|
||||||
{
|
|
||||||
result = (stbi_write_tga_to_func(write_func, fp.get(), width, height, 4, texture_data.data()) != 0);
|
|
||||||
}
|
|
||||||
else if (StringUtil::Strcasecmp(extension, ".bmp") == 0)
|
|
||||||
{
|
|
||||||
result = (stbi_write_bmp_to_func(write_func, fp.get(), width, height, 4, texture_data.data()) != 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!result)
|
|
||||||
{
|
{
|
||||||
Log_ErrorPrintf("Unknown extension in filename '%s' or save error: '%s'", filename.c_str(), extension);
|
Log_ErrorPrintf("Unknown extension in filename '%s' or save error: '%s'", filename.c_str(), extension);
|
||||||
return false;
|
return false;
|
||||||
|
@ -1990,43 +1938,11 @@ static bool CompressAndWriteTextureToFile(u32 width, u32 height, std::string fil
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GPU::WriteDisplayTextureToFile(std::string filename, bool full_resolution /* = true */,
|
bool GPU::WriteDisplayTextureToFile(std::string filename, bool compress_on_thread /* = false */)
|
||||||
bool apply_aspect_ratio /* = true */, bool compress_on_thread /* = false */)
|
|
||||||
{
|
{
|
||||||
if (!m_display_texture)
|
if (!m_display_texture)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
s32 resize_width = 0;
|
|
||||||
s32 resize_height = std::abs(m_display_texture_view_height);
|
|
||||||
if (apply_aspect_ratio)
|
|
||||||
{
|
|
||||||
const float ss_width_scale = static_cast<float>(m_display_active_width) / static_cast<float>(m_display_width);
|
|
||||||
const float ss_height_scale = static_cast<float>(m_display_active_height) / static_cast<float>(m_display_height);
|
|
||||||
const float ss_aspect_ratio = m_display_aspect_ratio * ss_width_scale / ss_height_scale;
|
|
||||||
resize_width = g_settings.display_stretch_vertically ?
|
|
||||||
m_display_texture_view_width :
|
|
||||||
static_cast<s32>(static_cast<float>(resize_height) * ss_aspect_ratio);
|
|
||||||
resize_height = g_settings.display_stretch_vertically ?
|
|
||||||
static_cast<s32>(static_cast<float>(resize_height) /
|
|
||||||
(m_display_aspect_ratio /
|
|
||||||
(static_cast<float>(m_display_width) / static_cast<float>(m_display_height)))) :
|
|
||||||
resize_height;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
resize_width = m_display_texture_view_width;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!full_resolution)
|
|
||||||
{
|
|
||||||
const s32 resolution_scale = std::abs(m_display_texture_view_height) / m_display_active_height;
|
|
||||||
resize_height /= resolution_scale;
|
|
||||||
resize_width /= resolution_scale;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (resize_width <= 0 || resize_height <= 0)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
const u32 read_x = static_cast<u32>(m_display_texture_view_x);
|
const u32 read_x = static_cast<u32>(m_display_texture_view_x);
|
||||||
const u32 read_y = static_cast<u32>(m_display_texture_view_y);
|
const u32 read_y = static_cast<u32>(m_display_texture_view_y);
|
||||||
const u32 read_width = static_cast<u32>(m_display_texture_view_width);
|
const u32 read_width = static_cast<u32>(m_display_texture_view_width);
|
||||||
|
@ -2034,13 +1950,14 @@ bool GPU::WriteDisplayTextureToFile(std::string filename, bool full_resolution /
|
||||||
|
|
||||||
const u32 texture_data_stride =
|
const u32 texture_data_stride =
|
||||||
Common::AlignUpPow2(GPUTexture::GetPixelSize(m_display_texture->GetFormat()) * read_width, 4);
|
Common::AlignUpPow2(GPUTexture::GetPixelSize(m_display_texture->GetFormat()) * read_width, 4);
|
||||||
std::vector<u8> texture_data(texture_data_stride * read_height);
|
std::vector<u32> texture_data((texture_data_stride * read_height) / sizeof(u32));
|
||||||
|
|
||||||
std::unique_ptr<GPUDownloadTexture> dltex;
|
std::unique_ptr<GPUDownloadTexture> dltex;
|
||||||
if (g_gpu_device->GetFeatures().memory_import)
|
if (g_gpu_device->GetFeatures().memory_import)
|
||||||
{
|
{
|
||||||
dltex = g_gpu_device->CreateDownloadTexture(read_width, read_height, m_display_texture->GetFormat(),
|
dltex =
|
||||||
texture_data.data(), texture_data.size(), texture_data_stride);
|
g_gpu_device->CreateDownloadTexture(read_width, read_height, m_display_texture->GetFormat(), texture_data.data(),
|
||||||
|
texture_data.size() * sizeof(u32), texture_data_stride);
|
||||||
}
|
}
|
||||||
if (!dltex)
|
if (!dltex)
|
||||||
{
|
{
|
||||||
|
@ -2074,21 +1991,19 @@ bool GPU::WriteDisplayTextureToFile(std::string filename, bool full_resolution /
|
||||||
if (!compress_on_thread)
|
if (!compress_on_thread)
|
||||||
{
|
{
|
||||||
return CompressAndWriteTextureToFile(read_width, read_height, std::move(filename), std::move(fp),
|
return CompressAndWriteTextureToFile(read_width, read_height, std::move(filename), std::move(fp),
|
||||||
g_settings.display_screenshot_quality, clear_alpha, flip_y, resize_width,
|
g_settings.display_screenshot_quality, clear_alpha, flip_y,
|
||||||
resize_height, std::move(texture_data), texture_data_stride,
|
std::move(texture_data), texture_data_stride, m_display_texture->GetFormat());
|
||||||
m_display_texture->GetFormat());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::thread compress_thread(CompressAndWriteTextureToFile, read_width, read_height, std::move(filename),
|
std::thread compress_thread(CompressAndWriteTextureToFile, read_width, read_height, std::move(filename),
|
||||||
std::move(fp), g_settings.display_screenshot_quality, clear_alpha, flip_y, resize_width,
|
std::move(fp), g_settings.display_screenshot_quality, clear_alpha, flip_y,
|
||||||
resize_height, std::move(texture_data), texture_data_stride,
|
std::move(texture_data), texture_data_stride, m_display_texture->GetFormat());
|
||||||
m_display_texture->GetFormat());
|
|
||||||
compress_thread.detach();
|
compress_thread.detach();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GPU::RenderScreenshotToBuffer(u32 width, u32 height, const Common::Rectangle<s32>& draw_rect, bool postfx,
|
bool GPU::RenderScreenshotToBuffer(u32 width, u32 height, const Common::Rectangle<s32>& draw_rect, bool postfx,
|
||||||
std::vector<u8>* out_pixels, u32* out_stride, GPUTexture::Format* out_format)
|
std::vector<u32>* out_pixels, u32* out_stride, GPUTexture::Format* out_format)
|
||||||
{
|
{
|
||||||
const GPUTexture::Format hdformat =
|
const GPUTexture::Format hdformat =
|
||||||
g_gpu_device->HasSurface() ? g_gpu_device->GetWindowFormat() : GPUTexture::Format::RGBA8;
|
g_gpu_device->HasSurface() ? g_gpu_device->GetWindowFormat() : GPUTexture::Format::RGBA8;
|
||||||
|
@ -2103,14 +2018,14 @@ bool GPU::RenderScreenshotToBuffer(u32 width, u32 height, const Common::Rectangl
|
||||||
// TODO: this should use copy shader instead.
|
// TODO: this should use copy shader instead.
|
||||||
RenderDisplay(render_texture.get(), draw_rect, postfx);
|
RenderDisplay(render_texture.get(), draw_rect, postfx);
|
||||||
|
|
||||||
const u32 stride = GPUTexture::GetPixelSize(hdformat) * width;
|
const u32 stride = Common::AlignUpPow2(GPUTexture::GetPixelSize(hdformat) * width, sizeof(u32));
|
||||||
out_pixels->resize(height * stride);
|
out_pixels->resize((height * stride) / sizeof(u32));
|
||||||
|
|
||||||
std::unique_ptr<GPUDownloadTexture> dltex;
|
std::unique_ptr<GPUDownloadTexture> dltex;
|
||||||
if (g_gpu_device->GetFeatures().memory_import)
|
if (g_gpu_device->GetFeatures().memory_import)
|
||||||
{
|
{
|
||||||
dltex =
|
dltex = g_gpu_device->CreateDownloadTexture(width, height, hdformat, out_pixels->data(),
|
||||||
g_gpu_device->CreateDownloadTexture(width, height, hdformat, out_pixels->data(), out_pixels->size(), stride);
|
out_pixels->size() * sizeof(u32), stride);
|
||||||
}
|
}
|
||||||
if (!dltex)
|
if (!dltex)
|
||||||
{
|
{
|
||||||
|
@ -2195,7 +2110,7 @@ bool GPU::RenderScreenshotToFile(std::string filename, DisplayScreenshotMode mod
|
||||||
if (width == 0 || height == 0)
|
if (width == 0 || height == 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
std::vector<u8> pixels;
|
std::vector<u32> pixels;
|
||||||
u32 pixels_stride;
|
u32 pixels_stride;
|
||||||
GPUTexture::Format pixels_format;
|
GPUTexture::Format pixels_format;
|
||||||
if (!RenderScreenshotToBuffer(width, height, draw_rect, !internal_resolution, &pixels, &pixels_stride,
|
if (!RenderScreenshotToBuffer(width, height, draw_rect, !internal_resolution, &pixels, &pixels_stride,
|
||||||
|
@ -2215,13 +2130,13 @@ bool GPU::RenderScreenshotToFile(std::string filename, DisplayScreenshotMode mod
|
||||||
if (!compress_on_thread)
|
if (!compress_on_thread)
|
||||||
{
|
{
|
||||||
return CompressAndWriteTextureToFile(width, height, std::move(filename), std::move(fp), quality, true,
|
return CompressAndWriteTextureToFile(width, height, std::move(filename), std::move(fp), quality, true,
|
||||||
g_gpu_device->UsesLowerLeftOrigin(), width, height, std::move(pixels),
|
g_gpu_device->UsesLowerLeftOrigin(), std::move(pixels), pixels_stride,
|
||||||
pixels_stride, pixels_format);
|
pixels_format);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::thread compress_thread(CompressAndWriteTextureToFile, width, height, std::move(filename), std::move(fp), quality,
|
std::thread compress_thread(CompressAndWriteTextureToFile, width, height, std::move(filename), std::move(fp), quality,
|
||||||
true, g_gpu_device->UsesLowerLeftOrigin(), width, height, std::move(pixels),
|
true, g_gpu_device->UsesLowerLeftOrigin(), std::move(pixels), pixels_stride,
|
||||||
pixels_stride, pixels_format);
|
pixels_format);
|
||||||
compress_thread.detach();
|
compress_thread.detach();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -2248,20 +2163,13 @@ bool GPU::DumpVRAMToFile(const char* filename)
|
||||||
|
|
||||||
bool GPU::DumpVRAMToFile(const char* filename, u32 width, u32 height, u32 stride, const void* buffer, bool remove_alpha)
|
bool GPU::DumpVRAMToFile(const char* filename, u32 width, u32 height, u32 stride, const void* buffer, bool remove_alpha)
|
||||||
{
|
{
|
||||||
auto fp = FileSystem::OpenManagedCFile(filename, "wb");
|
RGBA8Image image(width, height);
|
||||||
if (!fp)
|
|
||||||
{
|
|
||||||
Log_ErrorPrintf("Can't open file '%s'", filename);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto rgba8_buf = std::make_unique<u32[]>(width * height);
|
|
||||||
|
|
||||||
const char* ptr_in = static_cast<const char*>(buffer);
|
const char* ptr_in = static_cast<const char*>(buffer);
|
||||||
u32* ptr_out = rgba8_buf.get();
|
|
||||||
for (u32 row = 0; row < height; row++)
|
for (u32 row = 0; row < height; row++)
|
||||||
{
|
{
|
||||||
const char* row_ptr_in = ptr_in;
|
const char* row_ptr_in = ptr_in;
|
||||||
|
u32* ptr_out = image.GetRowPixels(row);
|
||||||
|
|
||||||
for (u32 col = 0; col < width; col++)
|
for (u32 col = 0; col < width; col++)
|
||||||
{
|
{
|
||||||
|
@ -2274,10 +2182,7 @@ bool GPU::DumpVRAMToFile(const char* filename, u32 width, u32 height, u32 stride
|
||||||
ptr_in += stride;
|
ptr_in += stride;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto write_func = [](void* context, void* data, int size) {
|
return image.SaveToFile(filename);
|
||||||
std::fwrite(data, 1, size, static_cast<std::FILE*>(context));
|
|
||||||
};
|
|
||||||
return (stbi_write_png_to_func(write_func, fp.get(), width, height, 4, rgba8_buf.get(), sizeof(u32) * width) != 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GPU::DrawDebugStateWindow()
|
void GPU::DrawDebugStateWindow()
|
||||||
|
|
|
@ -201,12 +201,11 @@ public:
|
||||||
Common::Rectangle<s32> CalculateDrawRect(s32 window_width, s32 window_height, bool apply_aspect_ratio = true) const;
|
Common::Rectangle<s32> CalculateDrawRect(s32 window_width, s32 window_height, bool apply_aspect_ratio = true) const;
|
||||||
|
|
||||||
/// Helper function to save current display texture to PNG.
|
/// Helper function to save current display texture to PNG.
|
||||||
bool WriteDisplayTextureToFile(std::string filename, bool full_resolution = true, bool apply_aspect_ratio = true,
|
bool WriteDisplayTextureToFile(std::string filename, bool compress_on_thread = false);
|
||||||
bool compress_on_thread = false);
|
|
||||||
|
|
||||||
/// Renders the display, optionally with postprocessing to the specified image.
|
/// Renders the display, optionally with postprocessing to the specified image.
|
||||||
bool RenderScreenshotToBuffer(u32 width, u32 height, const Common::Rectangle<s32>& draw_rect, bool postfx,
|
bool RenderScreenshotToBuffer(u32 width, u32 height, const Common::Rectangle<s32>& draw_rect, bool postfx,
|
||||||
std::vector<u8>* out_pixels, u32* out_stride, GPUTexture::Format* out_format);
|
std::vector<u32>* out_pixels, u32* out_stride, GPUTexture::Format* out_format);
|
||||||
|
|
||||||
/// Helper function to save screenshot to PNG.
|
/// Helper function to save screenshot to PNG.
|
||||||
bool RenderScreenshotToFile(std::string filename, DisplayScreenshotMode mode, u8 quality, bool compress_on_thread);
|
bool RenderScreenshotToFile(std::string filename, DisplayScreenshotMode mode, u8 quality, bool compress_on_thread);
|
||||||
|
|
|
@ -1471,20 +1471,14 @@ const char* Settings::GetDisplayScreenshotModeDisplayName(DisplayScreenshotMode
|
||||||
static constexpr const std::array s_display_screenshot_format_names = {
|
static constexpr const std::array s_display_screenshot_format_names = {
|
||||||
"PNG",
|
"PNG",
|
||||||
"JPEG",
|
"JPEG",
|
||||||
"TGA",
|
|
||||||
"BMP",
|
|
||||||
};
|
};
|
||||||
static constexpr const std::array s_display_screenshot_format_display_names = {
|
static constexpr const std::array s_display_screenshot_format_display_names = {
|
||||||
TRANSLATE_NOOP("Settings", "PNG"),
|
TRANSLATE_NOOP("Settings", "PNG"),
|
||||||
TRANSLATE_NOOP("Settings", "JPEG"),
|
TRANSLATE_NOOP("Settings", "JPEG"),
|
||||||
TRANSLATE_NOOP("Settings", "TGA"),
|
|
||||||
TRANSLATE_NOOP("Settings", "BMP"),
|
|
||||||
};
|
};
|
||||||
static constexpr const std::array s_display_screenshot_format_extensions = {
|
static constexpr const std::array s_display_screenshot_format_extensions = {
|
||||||
"png",
|
"png",
|
||||||
"jpg",
|
"jpg",
|
||||||
"tga",
|
|
||||||
"bmp",
|
|
||||||
};
|
};
|
||||||
|
|
||||||
std::optional<DisplayScreenshotFormat> Settings::ParseDisplayScreenshotFormat(const char* str)
|
std::optional<DisplayScreenshotFormat> Settings::ParseDisplayScreenshotFormat(const char* str)
|
||||||
|
|
|
@ -2446,7 +2446,7 @@ bool System::SaveStateToStream(ByteStream* state, u32 screenshot_size /* = 256 *
|
||||||
((display_aspect_ratio > 0.0f) ? display_aspect_ratio : 1.0f)));
|
((display_aspect_ratio > 0.0f) ? display_aspect_ratio : 1.0f)));
|
||||||
Log_VerbosePrintf("Saving %ux%u screenshot for state", screenshot_width, screenshot_height);
|
Log_VerbosePrintf("Saving %ux%u screenshot for state", screenshot_width, screenshot_height);
|
||||||
|
|
||||||
std::vector<u8> screenshot_buffer;
|
std::vector<u32> screenshot_buffer;
|
||||||
u32 screenshot_stride;
|
u32 screenshot_stride;
|
||||||
GPUTexture::Format screenshot_format;
|
GPUTexture::Format screenshot_format;
|
||||||
if (g_gpu->RenderScreenshotToBuffer(screenshot_width, screenshot_height,
|
if (g_gpu->RenderScreenshotToBuffer(screenshot_width, screenshot_height,
|
||||||
|
@ -2464,7 +2464,8 @@ bool System::SaveStateToStream(ByteStream* state, u32 screenshot_size /* = 256 *
|
||||||
{
|
{
|
||||||
if (g_gpu_device->UsesLowerLeftOrigin())
|
if (g_gpu_device->UsesLowerLeftOrigin())
|
||||||
{
|
{
|
||||||
GPUTexture::FlipTextureDataRGBA8(screenshot_width, screenshot_height, screenshot_buffer, screenshot_stride);
|
GPUTexture::FlipTextureDataRGBA8(screenshot_width, screenshot_height,
|
||||||
|
reinterpret_cast<u8*>(screenshot_buffer.data()), screenshot_stride);
|
||||||
}
|
}
|
||||||
|
|
||||||
header.offset_to_screenshot = static_cast<u32>(state->GetPosition());
|
header.offset_to_screenshot = static_cast<u32>(state->GetPosition());
|
||||||
|
|
|
@ -172,8 +172,6 @@ enum class DisplayScreenshotFormat : u8
|
||||||
{
|
{
|
||||||
PNG,
|
PNG,
|
||||||
JPEG,
|
JPEG,
|
||||||
TGA,
|
|
||||||
BMP,
|
|
||||||
Count
|
Count
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -230,7 +230,7 @@ bool GPUTexture::ValidateConfig(u32 width, u32 height, u32 layers, u32 levels, u
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GPUTexture::ConvertTextureDataToRGBA8(u32 width, u32 height, std::vector<u8>& texture_data,
|
bool GPUTexture::ConvertTextureDataToRGBA8(u32 width, u32 height, std::vector<u32>& texture_data,
|
||||||
u32& texture_data_stride, GPUTexture::Format format)
|
u32& texture_data_stride, GPUTexture::Format format)
|
||||||
{
|
{
|
||||||
switch (format)
|
switch (format)
|
||||||
|
@ -239,7 +239,7 @@ bool GPUTexture::ConvertTextureDataToRGBA8(u32 width, u32 height, std::vector<u8
|
||||||
{
|
{
|
||||||
for (u32 y = 0; y < height; y++)
|
for (u32 y = 0; y < height; y++)
|
||||||
{
|
{
|
||||||
u8* pixels = texture_data.data() + (y * texture_data_stride);
|
u8* pixels = reinterpret_cast<u8*>(texture_data.data()) + (y * texture_data_stride);
|
||||||
for (u32 x = 0; x < width; x++)
|
for (u32 x = 0; x < width; x++)
|
||||||
{
|
{
|
||||||
u32 pixel;
|
u32 pixel;
|
||||||
|
@ -258,12 +258,12 @@ bool GPUTexture::ConvertTextureDataToRGBA8(u32 width, u32 height, std::vector<u8
|
||||||
|
|
||||||
case Format::RGB565:
|
case Format::RGB565:
|
||||||
{
|
{
|
||||||
std::vector<u8> temp(width * height * sizeof(u32));
|
std::vector<u32> temp(width * height);
|
||||||
|
|
||||||
for (u32 y = 0; y < height; y++)
|
for (u32 y = 0; y < height; y++)
|
||||||
{
|
{
|
||||||
const u8* pixels_in = texture_data.data() + (y * texture_data_stride);
|
const u8* pixels_in = reinterpret_cast<const u8*>(texture_data.data()) + (y * texture_data_stride);
|
||||||
u8* pixels_out = &temp[y * width * sizeof(u32)];
|
u8* pixels_out = reinterpret_cast<u8*>(temp.data()) + (y * width * sizeof(u32));
|
||||||
|
|
||||||
for (u32 x = 0; x < width; x++)
|
for (u32 x = 0; x < width; x++)
|
||||||
{
|
{
|
||||||
|
@ -288,12 +288,12 @@ bool GPUTexture::ConvertTextureDataToRGBA8(u32 width, u32 height, std::vector<u8
|
||||||
|
|
||||||
case Format::RGBA5551:
|
case Format::RGBA5551:
|
||||||
{
|
{
|
||||||
std::vector<u8> temp(width * height * sizeof(u32));
|
std::vector<u32> temp(width * height);
|
||||||
|
|
||||||
for (u32 y = 0; y < height; y++)
|
for (u32 y = 0; y < height; y++)
|
||||||
{
|
{
|
||||||
const u8* pixels_in = texture_data.data() + (y * texture_data_stride);
|
const u8* pixels_in = reinterpret_cast<const u8*>(texture_data.data()) + (y * texture_data_stride);
|
||||||
u8* pixels_out = &temp[y * width];
|
u8* pixels_out = reinterpret_cast<u8*>(temp.data()) + (y * width * sizeof(u32));
|
||||||
|
|
||||||
for (u32 x = 0; x < width; x++)
|
for (u32 x = 0; x < width; x++)
|
||||||
{
|
{
|
||||||
|
@ -323,16 +323,16 @@ bool GPUTexture::ConvertTextureDataToRGBA8(u32 width, u32 height, std::vector<u8
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GPUTexture::FlipTextureDataRGBA8(u32 width, u32 height, std::vector<u8>& texture_data, u32 texture_data_stride)
|
void GPUTexture::FlipTextureDataRGBA8(u32 width, u32 height, u8* texture_data, u32 texture_data_stride)
|
||||||
{
|
{
|
||||||
std::vector<u8> temp(width * sizeof(u32));
|
std::unique_ptr<u8[]> temp = std::make_unique<u8[]>(texture_data_stride);
|
||||||
for (u32 flip_row = 0; flip_row < (height / 2); flip_row++)
|
for (u32 flip_row = 0; flip_row < (height / 2); flip_row++)
|
||||||
{
|
{
|
||||||
u8* top_ptr = &texture_data[flip_row * texture_data_stride];
|
u8* top_ptr = &texture_data[flip_row * texture_data_stride];
|
||||||
u8* bottom_ptr = &texture_data[((height - 1) - flip_row) * texture_data_stride];
|
u8* bottom_ptr = &texture_data[((height - 1) - flip_row) * texture_data_stride];
|
||||||
std::memcpy(temp.data(), top_ptr, texture_data_stride);
|
std::memcpy(temp.get(), top_ptr, texture_data_stride);
|
||||||
std::memcpy(top_ptr, bottom_ptr, texture_data_stride);
|
std::memcpy(top_ptr, bottom_ptr, texture_data_stride);
|
||||||
std::memcpy(bottom_ptr, temp.data(), texture_data_stride);
|
std::memcpy(bottom_ptr, temp.get(), texture_data_stride);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -89,9 +89,9 @@ public:
|
||||||
|
|
||||||
static bool ValidateConfig(u32 width, u32 height, u32 layers, u32 levels, u32 samples, Type type, Format format);
|
static bool ValidateConfig(u32 width, u32 height, u32 layers, u32 levels, u32 samples, Type type, Format format);
|
||||||
|
|
||||||
static bool ConvertTextureDataToRGBA8(u32 width, u32 height, std::vector<u8>& texture_data, u32& texture_data_stride,
|
static bool ConvertTextureDataToRGBA8(u32 width, u32 height, std::vector<u32>& texture_data, u32& texture_data_stride,
|
||||||
GPUTexture::Format format);
|
GPUTexture::Format format);
|
||||||
static void FlipTextureDataRGBA8(u32 width, u32 height, std::vector<u8>& texture_data, u32 texture_data_stride);
|
static void FlipTextureDataRGBA8(u32 width, u32 height, u8* texture_data, u32 texture_data_stride);
|
||||||
|
|
||||||
ALWAYS_INLINE u32 GetWidth() const { return m_width; }
|
ALWAYS_INLINE u32 GetWidth() const { return m_width; }
|
||||||
ALWAYS_INLINE u32 GetHeight() const { return m_height; }
|
ALWAYS_INLINE u32 GetHeight() const { return m_height; }
|
||||||
|
|
Loading…
Reference in a new issue