diff --git a/src/ReDIVA/app.cpp b/src/ReDIVA/app.cpp index 9c7d8c01..d354da33 100644 --- a/src/ReDIVA/app.cpp +++ b/src/ReDIVA/app.cpp @@ -95,17 +95,6 @@ #include #include -#ifndef USE_OPENGL -#if BAKE_PNG -#undef BAKE_PNG -#define BAKE_PNG (0) -#endif -#if BAKE_VIDEO -#undef BAKE_VIDEO -#define BAKE_VIDEO (0) -#endif -#endif - #ifdef DEBUG #define RENDER_DEBUG 1 #else @@ -379,11 +368,6 @@ int32_t app_main(const app_init_struct& ais) { Vulkan::use = ais.vulkan; #endif -#if BAKE_PNG || BAKE_VIDEO - if (Vulkan::use) - return -1; -#endif - if (app_init(ais)) { window_handle = glfwGetWin32Window(window); glfwShowWindow(window); @@ -527,16 +511,14 @@ bool app_render_data::load() { #endif #if BAKE_VIDEO - if (!Vulkan::use) { - if (!gladLoadWGLLoader((GLADloadproc)glfwGetProcAddress, GetDC(window_handle))) - return false; - - D3D11CreateDevice(0, D3D_DRIVER_TYPE_HARDWARE, 0, 0, 0, 0, - D3D11_SDK_VERSION, &d3d_device, 0, &d3d_device_context); - - if (GLAD_WGL_NV_DX_interop2) - d3d_gl_handle = wglDXOpenDeviceNV(d3d_device); - } + if (!Vulkan::use && !gladLoadWGLLoader((GLADloadproc)glfwGetProcAddress, GetDC(window_handle))) + return false; + + D3D11CreateDevice(0, D3D_DRIVER_TYPE_HARDWARE, 0, 0, 0, 0, + D3D11_SDK_VERSION, &d3d_device, 0, &d3d_device_context); + + if (!Vulkan::use && GLAD_WGL_NV_DX_interop2) + d3d_gl_handle = wglDXOpenDeviceNV(d3d_device); #endif gl_state_get_error(); diff --git a/src/ReDIVA/nvenc/nvenc_encoder.cpp b/src/ReDIVA/video/nvenc/nvenc_encoder.cpp similarity index 96% rename from src/ReDIVA/nvenc/nvenc_encoder.cpp rename to src/ReDIVA/video/nvenc/nvenc_encoder.cpp index 302c3091..e996f420 100644 --- a/src/ReDIVA/nvenc/nvenc_encoder.cpp +++ b/src/ReDIVA/video/nvenc/nvenc_encoder.cpp @@ -254,16 +254,16 @@ nvenc_encoder::~nvenc_encoder() { free_nvenc_dll(); } -void nvenc_encoder::end_encode(stream* s) { +void nvenc_encoder::end_encode(video_packet_handler* packet_handler) { NV_ENC_PIC_PARAMS pic_params = {}; pic_params.version = NV_ENC_PIC_PARAMS_VER; pic_params.encodePicFlags = NV_ENC_PIC_FLAG_EOS; print_nvenc_status(__LINE__, nvenc_api->nvEncEncodePicture(encoder, &pic_params)); - write_packet(s, false); + write_packet(packet_handler, false); } -void nvenc_encoder::encode_frame(stream* s, void* texture) { +void nvenc_encoder::encode_frame(video_packet_handler* packet_handler, void* texture) { const int32_t index = frame % num_buffers; ((ID3D11DeviceContext*)d3d_device_context)->CopyResource( @@ -287,7 +287,7 @@ void nvenc_encoder::encode_frame(stream* s, void* texture) { if (encode_status == NV_ENC_SUCCESS || encode_status == NV_ENC_ERR_NEED_MORE_INPUT) { frame++; - write_packet(s, true); + write_packet(packet_handler, true); } else print_nvenc_status(__LINE__, encode_status); @@ -327,8 +327,13 @@ void nvenc_encoder::unmap_resource(int32_t index) { mapped_resources[index] = 0; } -void nvenc_encoder::write_packet(stream* s, bool output_delay) { +void nvenc_encoder::write_packet(video_packet_handler* packet_handler, bool output_delay) { const int32_t end_encoded_frame = output_delay ? frame - this->output_delay : frame; + + packet_handler->reset(); + packet_handler->allocate((ssize_t)end_encoded_frame - encoded_frame); + + size_t packet_index = 0; for (; encoded_frame < end_encoded_frame; encoded_frame++) { const int32_t index = encoded_frame % num_buffers; @@ -338,9 +343,9 @@ void nvenc_encoder::write_packet(stream* s, bool output_delay) { lock_params.doNotWait = false; print_nvenc_status(__LINE__, nvenc_api->nvEncLockBitstream(encoder, &lock_params)); - s->write(lock_params.bitstreamBufferPtr, lock_params.bitstreamSizeInBytes); + packet_handler->set_data(packet_index++, + lock_params.bitstreamBufferPtr, lock_params.bitstreamSizeInBytes); print_nvenc_status(__LINE__, nvenc_api->nvEncUnlockBitstream(encoder, lock_params.outputBitstream)); - s->flush(); unmap_resource(index); } diff --git a/src/ReDIVA/nvenc/nvenc_encoder.hpp b/src/ReDIVA/video/nvenc/nvenc_encoder.hpp similarity index 80% rename from src/ReDIVA/nvenc/nvenc_encoder.hpp rename to src/ReDIVA/video/nvenc/nvenc_encoder.hpp index fbb57186..ef0570e1 100644 --- a/src/ReDIVA/nvenc/nvenc_encoder.hpp +++ b/src/ReDIVA/video/nvenc/nvenc_encoder.hpp @@ -5,9 +5,9 @@ #pragma once -#include "../../KKdLib/default.hpp" -#include "../../KKdLib/io/stream.hpp" -#include +#include "../../../KKdLib/default.hpp" +#include "../../../KKdLib/prj/stack_allocator.hpp" +#include "../video_packet.hpp" #include enum nvenc_encoder_format { @@ -56,12 +56,12 @@ public: int32_t num_bframes = 3, int32_t lookahead_depth = 15, int32_t extra_output_delay = 3); ~nvenc_encoder(); - void end_encode(stream* s); - void encode_frame(stream* s, void* texture); + void end_encode(video_packet_handler* packet_handler); + void encode_frame(video_packet_handler* packet_handler, void* texture); private: int32_t get_capability_value(NV_ENC_CAPS caps_to_query); void map_resource(int32_t index); void unmap_resource(int32_t index); - void write_packet(stream* s, bool output_delay); + void write_packet(video_packet_handler* packet_handler, bool output_delay); }; diff --git a/src/ReDIVA/video/video_packet.cpp b/src/ReDIVA/video/video_packet.cpp new file mode 100644 index 00000000..dd9f64ca --- /dev/null +++ b/src/ReDIVA/video/video_packet.cpp @@ -0,0 +1,45 @@ +/* + by korenkonder + GitHub/GitLab: korenkonder +*/ + +#include "video_packet.hpp" + +video_packet_handler::video_packet_handler() : num_packet(), packets() { + alloc.size = 0x1000000; +} + +video_packet_handler::~video_packet_handler() { + +} + +void video_packet_handler::allocate(ssize_t num_packet) { + this->num_packet = max_def(num_packet, 0); + packets = alloc.allocate(this->num_packet); +} + +void video_packet_handler::reset() { + num_packet = 0; + packets = 0; + alloc.reset(); +} + +void video_packet_handler::set_data(size_t packet_index, const void* data, size_t size) { + video_packet& packet = packets[packet_index]; + if (!packet.data || packet.data && packet.size < size) + packet.data = alloc.allocate(size); + packet.size = size; + memcpy(packet.data, data, size); +} + +void video_packet_handler::write(stream* s) { + for (ssize_t i = 0; i < num_packet; i++) { + video_packet& packet = packets[i]; + if (!packet.data || !packet.size) + continue; + + s->write(packet.data, packet.size); + } + + reset(); +} diff --git a/src/ReDIVA/video/video_packet.hpp b/src/ReDIVA/video/video_packet.hpp new file mode 100644 index 00000000..3992487d --- /dev/null +++ b/src/ReDIVA/video/video_packet.hpp @@ -0,0 +1,29 @@ +/* + by korenkonder + GitHub/GitLab: korenkonder +*/ + +#pragma once + +#include "../../KKdLib/default.hpp" +#include "../../KKdLib/io/stream.hpp" +#include "../../KKdLib/prj/stack_allocator.hpp" + +struct video_packet { + void* data; + size_t size; +}; + +struct video_packet_handler { + prj::stack_allocator alloc; + ssize_t num_packet; + video_packet* packets; + + video_packet_handler(); + ~video_packet_handler(); + + void allocate(ssize_t num_packet); + void reset(); + void set_data(size_t packet_index, const void* data, size_t size); + void write(stream* s); +}; diff --git a/src/ReDIVA/x_pv_game.cpp b/src/ReDIVA/x_pv_game.cpp index 31ffeceb..fe064578 100644 --- a/src/ReDIVA/x_pv_game.cpp +++ b/src/ReDIVA/x_pv_game.cpp @@ -31,17 +31,6 @@ #include "../KKdLib/sort.hpp" #include "../KKdLib/str_utils.hpp" -#ifndef USE_OPENGL -#if BAKE_PNG -#undef BAKE_PNG -#define BAKE_PNG (0) -#endif -#if BAKE_VIDEO -#undef BAKE_VIDEO -#define BAKE_VIDEO (0) -#endif -#endif - #if BAKE_X_PACK #include "../KKdLib/f2/header.hpp" #include "../KKdLib/io/memory_stream.hpp" @@ -57,7 +46,8 @@ #include #include #include "../CRE/shader_dev.hpp" -#include "nvenc/nvenc_encoder.hpp" +#include "video/nvenc/nvenc_encoder.hpp" +#include "video/video_packet.hpp" #endif #if BAKE_PV826 #include "../KKdLib/waitable_timer.hpp" @@ -6284,6 +6274,7 @@ bool x_pv_game::init() { #if BAKE_PNG || BAKE_VIDEO static bool img_write = false; +static bool bake_image = false; #endif #if BAKE_VIDEO @@ -6291,27 +6282,39 @@ extern ID3D11Device* d3d_device; extern ID3D11DeviceContext* d3d_device_context; extern HANDLE d3d_gl_handle; -struct d3d_gl_rbo_struct { - GLuint glid; +struct d3d_gl_fbo_tex_struct { + GLuint fbo; + GLuint tex; HANDLE handle; - inline d3d_gl_rbo_struct() : glid(), handle() { + inline d3d_gl_fbo_tex_struct() : tex(), handle() { } inline void init(ID3D11Texture2D* texture) { - glGenRenderbuffers(1, &glid); + glGenTextures(1, &tex); handle = wglDXRegisterObjectNV(d3d_gl_handle, texture, - glid, GL_RENDERBUFFER, WGL_ACCESS_READ_WRITE_NV); + tex, GL_TEXTURE_2D, WGL_ACCESS_READ_WRITE_NV); + + glGenFramebuffers(1, &fbo); + + lock(); + gl_state_bind_framebuffer(fbo); + glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, tex, 0); + gl_state_bind_framebuffer(0); + unlock(); } inline void free() { + glDeleteFramebuffers(1, &fbo); + fbo = 0; + wglDXUnregisterObjectNV(d3d_gl_handle, handle); handle = 0; - glDeleteRenderbuffers(1, &glid); - glid = 0; + glDeleteTextures(1, &tex); + tex = 0; } inline void lock() { @@ -6323,36 +6326,34 @@ struct d3d_gl_rbo_struct { } }; -ID3D11Texture2D* d3d_texture; - -GLuint d3d_gl_fbo; -d3d_gl_rbo_struct d3d_gl_rbo; +static ID3D11Texture2D* d3d_texture; +static d3d_gl_fbo_tex_struct d3d_gl_fbo_tex; #if BAKE_VIDEO_ALPHA -ID3D11Texture2D* d3d_alpha_texture; - -GLuint d3d_gl_alpha_fbo; -d3d_gl_rbo_struct d3d_gl_alpha_rbo; +static ID3D11Texture2D* d3d_alpha_texture; +static d3d_gl_fbo_tex_struct d3d_gl_alpha_fbo_tex; #endif waitable_timer d3d_timer; -const size_t nvenc_src_pixel_size = 4; -const size_t nvenc_dst_pixel_size = 4; +static const size_t nvenc_src_pixel_size = 4; +static const size_t nvenc_dst_pixel_size = 4; -nvenc_encoder_format nvenc_format = NVENC_ENCODER_YUV420_10BIT; -bool nvenc_lossless = false; +static nvenc_encoder_format nvenc_format; +static bool nvenc_lossless; -bool nvenc_rgb; +static bool nvenc_rgb; -std::vector nvenc_temp_pixels; +static std::vector nvenc_temp_pixels; -nvenc_encoder* nvenc_enc; -file_stream* nvenc_stream; +static nvenc_encoder* nvenc_enc; +static file_stream* nvenc_stream; +static video_packet_handler* nvenc_video_packets; #if BAKE_VIDEO_ALPHA -nvenc_encoder* nvenc_alpha_enc; -file_stream* nvenc_alpha_stream; +static nvenc_encoder* nvenc_alpha_enc; +static file_stream* nvenc_alpha_stream; +static video_packet_handler* nvenc_alpha_video_packets; #endif #endif @@ -6397,9 +6398,9 @@ bool x_pv_game::ctrl() { #if BAKE_VIDEO if (GLAD_WGL_NV_DX_interop2) { - d3d_gl_rbo.lock(); + d3d_gl_fbo_tex.lock(); - gl_state_bind_framebuffer(d3d_gl_fbo); + gl_state_bind_framebuffer(d3d_gl_fbo_tex.fbo); gl_state_active_bind_texture_2d(0, tex->glid); if (nvenc_rgb) { @@ -6421,13 +6422,13 @@ bool x_pv_game::ctrl() { gl_state_bind_texture_2d(0); gl_state_bind_framebuffer(0); - d3d_gl_rbo.unlock(); + d3d_gl_fbo_tex.unlock(); #if BAKE_VIDEO_ALPHA - d3d_gl_alpha_rbo.lock(); + d3d_gl_alpha_fbo_tex.lock(); - gl_state_bind_framebuffer(d3d_gl_alpha_fbo); - gl_state_active_bind_texture_2d(0, tex->glid); + gl_state_bind_framebuffer(d3d_gl_alpha_fbo_tex.fbo); + gl_state_active_bind_texture_2d(0, tex->tex); GLint swizzle_aaa1[] = { GL_ALPHA, GL_ALPHA, GL_ALPHA, GL_ONE }; glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, swizzle_aaa1); @@ -6444,7 +6445,7 @@ bool x_pv_game::ctrl() { gl_state_bind_texture_2d(0); gl_state_bind_framebuffer(0); - d3d_gl_alpha_rbo.unlock(); + d3d_gl_alpha_fbo_tex.unlock(); #endif } else { @@ -6487,7 +6488,7 @@ bool x_pv_game::ctrl() { HRESULT result_alpha = d3d_device_context->Map(d3d_alpha_texture, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped_alpha_res); if (SUCCEEDED(result) && SUCCEEDED(result_alpha)) { - gl_state_bind_texture_2d(tex->glid); + gl_state_bind_texture_2d(tex->tex); glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_HALF_FLOAT, nvenc_temp_pixels.data()); gl_state_bind_texture_2d(0); @@ -6540,14 +6541,19 @@ bool x_pv_game::ctrl() { #endif } - nvenc_enc->encode_frame(nvenc_stream, d3d_texture); + nvenc_enc->encode_frame(nvenc_video_packets, d3d_texture); #if BAKE_VIDEO_ALPHA - nvenc_alpha_enc->encode_frame(nvenc_alpha_stream, d3d_alpha_texture); + nvenc_alpha_enc->encode_frame(nvenc_alpha_video_packets, d3d_alpha_texture); +#endif + + nvenc_video_packets->write(nvenc_stream); +#if BAKE_VIDEO_ALPHA + nvenc_alpha_video_packets->write(nvenc_alpha_stream); #endif #elif BAKE_PNG std::vector temp_pixels; temp_pixels.resize((size_t)width * (size_t)height * 4 * sizeof(uint8_t)); - gl_state_bind_texture_2d(tex->glid); + gl_state_bind_texture_2d(tex->tex); glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, temp_pixels.data()); gl_state_bind_texture_2d(0); @@ -7922,57 +7928,40 @@ bool x_pv_game::ctrl() { } if (GLAD_WGL_NV_DX_interop2) { - d3d_gl_rbo.init(d3d_texture); - - glGenFramebuffers(1, &d3d_gl_fbo); - - d3d_gl_rbo.lock(); - gl_state_bind_framebuffer(d3d_gl_fbo); - glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, d3d_gl_rbo.glid); - gl_state_bind_framebuffer(0); - d3d_gl_rbo.unlock(); - + d3d_gl_fbo_tex.init(d3d_texture); #if BAKE_VIDEO_ALPHA - d3d_gl_alpha_rbo.init(d3d_alpha_texture); - - glGenFramebuffers(1, &d3d_gl_alpha_fbo); - - d3d_gl_alpha_rbo.lock(); - gl_state_bind_framebuffer(d3d_gl_alpha_fbo); - glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, d3d_gl_alpha_rbo.glid); - gl_state_bind_framebuffer(0); - d3d_gl_alpha_rbo.unlock(); + d3d_gl_alpha_fbo_tex.init(d3d_alpha_texture); #endif } if (!GLAD_WGL_NV_DX_interop2) nvenc_temp_pixels.resize((size_t)width * (size_t)height * nvenc_src_pixel_size); -#if BAKE_VIDEO_ALPHA nvenc_enc = new nvenc_encoder(width, height, d3d_device, d3d_device_context, nvenc_format, nvenc_lossless); - nvenc_alpha_enc = new nvenc_encoder(width, height, - d3d_device, d3d_device_context, nvenc_format, nvenc_lossless); char buf[0x100]; +#if BAKE_VIDEO_ALPHA sprintf_s(buf, sizeof(buf), "G:\\ReDIVA\\Videos\\ReDIVA_pv%03d_color.265", get_data().pv_id); +#else + sprintf_s(buf, sizeof(buf), "G:\\ReDIVA\\Videos\\ReDIVA_pv%03d.265", get_data().pv_id); +#endif nvenc_stream = new file_stream(); nvenc_stream->open(buf, "wb"); + nvenc_video_packets = new video_packet_handler; + +#if BAKE_VIDEO_ALPHA + nvenc_alpha_enc = new nvenc_encoder(width, height, + d3d_device, d3d_device_context, nvenc_format, nvenc_lossless); + sprintf_s(buf, sizeof(buf), "G:\\ReDIVA\\Videos\\ReDIVA_pv%03d_alpha.265", get_data().pv_id); nvenc_alpha_stream = new file_stream(); nvenc_alpha_stream->open(buf, "wb"); -#else - nvenc_enc = new nvenc_encoder(width, height, - d3d_device, d3d_device_context, nvenc_format, nvenc_lossless); - char buf[0x100]; - sprintf_s(buf, sizeof(buf), "G:\\ReDIVA\\Videos\\ReDIVA_pv%03d.265", get_data().pv_id); - - nvenc_stream = new file_stream(); - nvenc_stream->open(buf, "wb"); + nvenc_alpha_video_packets = new video_packet_handler; #endif #endif #endif @@ -8015,14 +8004,22 @@ bool x_pv_game::ctrl() { disable_cursor = false; #if BAKE_VIDEO_ALPHA - nvenc_alpha_enc->end_encode(nvenc_alpha_stream); + nvenc_alpha_enc->end_encode(nvenc_alpha_video_packets); + nvenc_alpha_video_packets->write(nvenc_alpha_stream); + + delete nvenc_alpha_video_packets; + nvenc_alpha_video_packets = 0; nvenc_alpha_stream->close(); delete nvenc_alpha_stream; nvenc_alpha_stream = 0; #endif - nvenc_enc->end_encode(nvenc_stream); + nvenc_enc->end_encode(nvenc_video_packets); + nvenc_video_packets->write(nvenc_stream); + + delete nvenc_video_packets; + nvenc_video_packets = 0; nvenc_stream->close(); delete nvenc_stream; @@ -8038,16 +8035,9 @@ bool x_pv_game::ctrl() { if (GLAD_WGL_NV_DX_interop2) { #if BAKE_VIDEO_ALPHA - glDeleteFramebuffers(1, &d3d_gl_alpha_fbo); - d3d_gl_alpha_fbo = 0; - - d3d_gl_alpha_rbo.free(); + d3d_gl_alpha_fbo_tex.free(); #endif - - glDeleteFramebuffers(1, &d3d_gl_fbo); - d3d_gl_fbo = 0; - - d3d_gl_rbo.free(); + d3d_gl_fbo_tex.free(); } #if BAKE_VIDEO_ALPHA @@ -10269,6 +10259,10 @@ XPVGameSelector::~XPVGameSelector() { bool XPVGameSelector::init() { start = false; exit = false; +#if BAKE_VIDEO + nvenc_format = NVENC_ENCODER_YUV420_10BIT; + nvenc_lossless = false; +#endif return true; } @@ -10458,6 +10452,29 @@ void XPVGameSelector::window() { ImGui::EndPropertyColumn(); } +#if BAKE_VIDEO + ImGui::Checkbox("Bake Video", &bake_image); + + ImGui::DisableElementPush(bake_image); + if (ImGui::BeginChild("Video")) { + const char* nvenc_format_names[] = { + "NV12 (YUV420)", + "YUV420 10 bit", + "YUV444", + "YUV444 10 bit", + "RGB", + "RGB 10 bit", + }; + + ImGui::ColumnComboBox("Format", nvenc_format_names, 6, (int32_t*)&nvenc_format, 0, false, &focus); + ImGui::Checkbox("Lossless", &nvenc_lossless); + ImGui::EndChild(); + } + ImGui::DisableElementPop(bake_image); +#elif BAKE_PNG + ImGui::Checkbox("Bake PNG", &bake_image); +#endif + if (ImGui::Button("Start")) { start = true; exit = true;