Capture final frame color buffer to file

More
11 months 3 weeks ago - 11 months 3 weeks ago #1 by KianA Capture final frame color buffer to file was created by KianA
Hi @Crosire,

I am working on a project which involves capturing raw uncompressed Game frames. And at a later time, pass them through FFmpeg for encoding.
Is there a way I can capture such raw uncompressed frames (final frame color buffer) using the reshade application irrespective of the graphics API used by the Game ?
It is fine if it works for just dx11 games.
Also, it would be helpful if you can tell any libraries that would let me dump the frames into a file.

P.S.: I don't have access to 4K Monitor but I want to capture 4K frame color buffer rendered by NVIDIA GPU.

Thanks in advance!


 
Last edit: 11 months 3 weeks ago by KianA.

Please Log in or Create an account to join the conversation.

More
11 months 3 weeks ago - 11 months 3 weeks ago #2 by crosire Replied by crosire on topic Capture final frame color buffer to file
It's possible in ReShade 5 (would need to build current ReShade source from GitHub, see also github.com/crosire/reshade/tree/main/include ) with an add-on.

Will see if I can add an example (to github.com/crosire/reshade/tree/main/examples ) that shows how to write an add-on that captures the frame data and encodes it into a video with ffmpeg.

For now, here is a quick example (compile this to a DLL, rename the file extension from ".dll" to ".addon" and put it in the same directory as a ReShade 5 build for it to load) of how to get the raw data of the current frame on the CPU at least (not particularly fast code, but shows the general flow):
#include <cassert>
#include <reshade.hpp>

struct user_data
{
    static const uint8_t GUID[16] = { 0x0D, 0x75, 0x25, 0xF9, 0xC4, 0xE1, 0x42, 0x6E, 0xBC, 0x99, 0x15, 0xBB, 0xD5, 0xFD, 0x51, 0xF2 };

    reshade::api::resource host_resource = { 0 };
};

static void on_init(reshade::api::swapchain *swapchain)
{
    user_data &data = swapchain->create_user_data<user_data>;(user_data::GUID);

    reshade::api::device *const device = swapchain->get_device();

    // Get description of the back buffer resources
    const reshade::api::resource_desc desc = device->get_resource_desc(swapchain->get_current_back_buffer());

    // Create a CPU-accessible texture with matching dimensions
    if (!device->create_resource(
            reshade::api::resource_desc(
                desc.texture.width,
                desc.texture.height,
                1,
                1,
                desc.texture.format,
                1,
                reshade::api::memory_heap::gpu_to_cpu,
                reshade::api::resource_usage::copy_dest),
            nullptr,
            reshade::api::resource_usage::cpu_access,
            &data.host_resource))
    {
        reshade::log_message(1, "Failed to create host resource");
        return;
    }
}
static void on_destroy(reshade::api::swapchain *swapchain)
{
    user_data &data = swapchain->get_user_data<user_data>;(user_data::GUID);

    if (data.host_resource != 0)
    {
        swapchain->get_device()->destroy_resource(data.host_resource);
    }

    swapchain->destroy_user_data<user_data>;(user_data::GUID);
}

static void on_present(reshade::api::command_queue *queue, reshade::api::swapchain *swapchain)
{
    user_data &data = swapchain->get_user_data<user_data>;(user_data::GUID);

    reshade::api::device *const device = swapchain->get_device();

    reshade::api::command_list *cmd_list = queue->get_immediate_command_list();
    // TODO: Add barriers/state transitions for DX12/Vulkan support (using "cmd_list->barrier()")
    // Copy current frame into the CPU-accessible texture
    cmd_list->copy_resource(swapchain->get_current_back_buffer(), data.host_resource);
    // Very slow ... but ensures the copy has completed before accessing the data next
    queue->wait_idle();

    // Map CPU-accessible texture to read the data
    reshade::api::subresource_data host_data;
    if (!device->map_texture_region(
            data.host_resource, 0, nullptr, reshade::api::map_access::read_only, &host_data))
        return;

    const reshade::api::resource_desc desc = device->get_resource_desc(data.host_resource);

    // TODO: This assumes that the format is RGBA8, need to handle differently for different formats
    assert(desc.texture.format == reshade::api::format::r8g8b8a8_unorm);
    for (int y = 0; y < desc.texture.height; ++y)
    {
        for (int x = 0; x < desc.texture.width; ++x)
        {
            const size_t host_data_index = y * host_data.row_pitch + x * 4;

            const uint8_t r = static_cast<const uint8_t *>;(host_data.data)[host_data_index + 0];
            const uint8_t g = static_cast<const uint8_t *>;(host_data.data)[host_data_index + 1];
            const uint8_t b = static_cast<const uint8_t *>;(host_data.data)[host_data_index + 2];
            const uint8_t a = static_cast<const uint8_t *>;(host_data.data)[host_data_index + 3];

            // TODO: Do something with the pixel, e.g. dump this whole image to an image file
        }
    }

    device->unmap_texture_region(data.host_resource, 0);
}

BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID)
{
    switch (fdwReason)
    {
    case DLL_PROCESS_ATTACH:
        if (!reshade::register_addon(hinstDLL))
            return FALSE;
        reshade::register_event<reshade::addon_event::init_swapchain>;(on_init);
        reshade::register_event<reshade::addon_event::destroy_swapchain>;(on_destroy);
        reshade::register_event<reshade::addon_event::present>;(on_present);
        break;
    case DLL_PROCESS_DETACH:
        reshade::unregister_addon(hinstDLL);
        break;
    }
    return TRUE;
}
Last edit: 11 months 3 weeks ago by crosire.

Please Log in or Create an account to join the conversation.

More
10 months 4 weeks ago #3 by crosire Replied by crosire on topic Capture final frame color buffer to file
There is a fully fledged example using ffmpeg here now: github.com/crosire/reshade/blob/main/exa...re/video_capture.cpp

Please Log in or Create an account to join the conversation.

We use cookies

We use cookies on our website. Some of them are essential for the operation of the site, while others help us to improve this site and the user experience (tracking cookies). You can decide for yourself whether you want to allow cookies or not. Please note that if you reject them, you may not be able to use all the functionalities of the site.