NVENC: Handle color conversion by ourself on GPU

This commit is contained in:
korenkonder
2025-01-29 01:34:08 +03:00
parent 1664951045
commit eac016fcf8
4 changed files with 216 additions and 134 deletions
+90
View File
@@ -11,12 +11,39 @@
enum shader_dev_sub_enum {
SHADER_DEV_SUB_SHADER_FFP = 0,
SHADER_DEV_SUB_CONVERT_YCBCR_BT709,
SHADER_DEV_SUB_CONVERT_RGB,
SHADER_DEV_SUB_CONVERT_ALPHA,
#if DISPLAY_IBL
SHADER_DEV_SUB_CUBEMAP_DISPLAY,
#endif
SHADER_DEV_SUB_SHADER_END,
};
static const int32_t convert_ycbcr_bt709_vpt_unival_max[] = {
-1,
};
static const int32_t convert_ycbcr_bt709_fpt_unival_max[] = {
-1,
};
static const int32_t convert_alpha_vpt_unival_max[] = {
-1,
};
static const int32_t convert_alpha_fpt_unival_max[] = {
-1,
};
static const int32_t convert_rgb_vpt_unival_max[] = {
-1,
};
static const int32_t convert_rgb_fpt_unival_max[] = {
-1,
};
#if DISPLAY_IBL
static const int32_t cubemap_display_vpt_unival_max[] = {
-1,
@@ -44,6 +71,39 @@ static const int32_t cubemap_display_fpt_unival_max[] = {
#define SHADER_DESCRIPTION_COMMON_QUAD \
{ SHADER_DESCRIPTION_UNIFORM, 0, sizeof(quad_shader_data), U_INVALID, }
static const shader_description convert_ycbcr_bt709_vpt_desc[] = {
SHADER_DESCRIPTION_COMMON_QUAD,
{ SHADER_DESCRIPTION_END, -1, -1, U_INVALID, },
};
static const shader_description convert_ycbcr_bt709_fpt_desc[] = {
{ SHADER_DESCRIPTION_SAMPLER, 0, 0, U_INVALID, },
{ SHADER_DESCRIPTION_FRAGMENT_OUTPUT, 0, 4, U_INVALID, },
{ SHADER_DESCRIPTION_END, -1, -1, U_INVALID, },
};
static const shader_description convert_alpha_vpt_desc[] = {
SHADER_DESCRIPTION_COMMON_QUAD,
{ SHADER_DESCRIPTION_END, -1, -1, U_INVALID, },
};
static const shader_description convert_alpha_fpt_desc[] = {
{ SHADER_DESCRIPTION_SAMPLER, 0, 0, U_INVALID, },
{ SHADER_DESCRIPTION_FRAGMENT_OUTPUT, 0, 4, U_INVALID, },
{ SHADER_DESCRIPTION_END, -1, -1, U_INVALID, },
};
static const shader_description convert_rgb_vpt_desc[] = {
SHADER_DESCRIPTION_COMMON_QUAD,
{ SHADER_DESCRIPTION_END, -1, -1, U_INVALID, },
};
static const shader_description convert_rgb_fpt_desc[] = {
{ SHADER_DESCRIPTION_SAMPLER, 0, 0, U_INVALID, },
{ SHADER_DESCRIPTION_FRAGMENT_OUTPUT, 0, 4, U_INVALID, },
{ SHADER_DESCRIPTION_END, -1, -1, U_INVALID, },
};
#if DISPLAY_IBL
static const shader_description cubemap_display_vpt_desc[] = {
{ SHADER_DESCRIPTION_VERTEX_INPUT, 0, 3, U_INVALID, },
@@ -65,6 +125,18 @@ static const shader_description cubemap_display_fpt_desc[] = {
#undef SHADER_DESCRIPTION_EFFECT_TEXTURE_SAMPLER
#pragma endregion
static const uniform_name CONVERT_YCBCR_BT709_uniform[] = {
U_INVALID,
};
static const uniform_name CONVERT_ALPHA_uniform[] = {
U_INVALID,
};
static const uniform_name CONVERT_RGB_uniform[] = {
U_INVALID,
};
#if DISPLAY_IBL
static const uniform_name CUBEMAP_DISPLAY_uniform[] = {
U_INVALID,
@@ -82,6 +154,21 @@ static const uniform_name CUBEMAP_DISPLAY_uniform[] = {
fp##_fpt_desc, \
}
static const shader_sub_table CONVERT_YCBCR_BT709_table[] = {
shader_sub_table_struct(CONVERT_YCBCR_BT709,
convert_ycbcr_bt709, convert_ycbcr_bt709),
};
static const shader_sub_table CONVERT_ALPHA_table[] = {
shader_sub_table_struct(CONVERT_ALPHA,
convert_alpha, convert_alpha),
};
static const shader_sub_table CONVERT_RGB_table[] = {
shader_sub_table_struct(CONVERT_RGB,
convert_rgb, convert_rgb),
};
#if DISPLAY_IBL
static const shader_sub_table CUBEMAP_DISPLAY_table[] = {
shader_sub_table_struct(CUBEMAP_DISPLAY,
@@ -110,6 +197,9 @@ const shader_table shader_dev_table[] = {
0,
0,
},
shader_table_struct(CONVERT_YCBCR_BT709),
shader_table_struct(CONVERT_ALPHA),
shader_table_struct(CONVERT_RGB),
#if DISPLAY_IBL
shader_table_struct(CUBEMAP_DISPLAY),
#endif
+4
View File
@@ -11,6 +11,10 @@
enum shader_dev_enum {
SHADER_DEV_FFP = 0,
SHADER_DEV_CONVERT_YCBCR_BT709,
SHADER_DEV_CONVERT_ALPHA,
SHADER_DEV_CONVERT_RGB,
SHADER_DEV_CONVERT_YCGCO,
#if DISPLAY_IBL
SHADER_DEV_CUBEMAP_DISPLAY,
#endif
+5 -3
View File
@@ -85,7 +85,9 @@ nvenc_encoder::nvenc_encoder(int32_t width, int32_t height,
is_10_bit &= support_10bit_encode;
is_444 &= support_yuv444_encode;
buffer_format = is_rgb ? NV_ENC_BUFFER_FORMAT_AYUV : NV_ENC_BUFFER_FORMAT_ARGB;
is_10_bit &= !lossless;
buffer_format = NV_ENC_BUFFER_FORMAT_ARGB;
num_bframes = clamp_def(num_bframes, 0, num_max_bframes);
extra_output_delay = max_def(extra_output_delay, 2);
@@ -128,7 +130,7 @@ nvenc_encoder::nvenc_encoder(int32_t width, int32_t height,
if (!lossless)
hevc_config.useBFramesAsRef = (NV_ENC_BFRAME_REF_MODE)(support_bframe_ref_mode & NV_ENC_BFRAME_REF_MODE_MIDDLE);
hevc_config.outputBitDepth = is_10_bit ? NV_ENC_BIT_DEPTH_10 : NV_ENC_BIT_DEPTH_8;
hevc_config.inputBitDepth = is_10_bit ? NV_ENC_BIT_DEPTH_10 : NV_ENC_BIT_DEPTH_8;
hevc_config.inputBitDepth = NV_ENC_BIT_DEPTH_8;
hevc_config.hevcVUIParameters.videoSignalTypePresentFlag = 1;
hevc_config.hevcVUIParameters.videoFormat = NV_ENC_VUI_VIDEO_FORMAT_UNSPECIFIED;
hevc_config.hevcVUIParameters.videoFullRangeFlag = 1;
@@ -181,7 +183,7 @@ nvenc_encoder::nvenc_encoder(int32_t width, int32_t height,
tex_desc.Height = height;
tex_desc.MipLevels = 1;
tex_desc.ArraySize = 1;
tex_desc.Format = is_rgb ? DXGI_FORMAT_AYUV : DXGI_FORMAT_R8G8B8A8_UNORM;
tex_desc.Format = DXGI_FORMAT_AYUV;
tex_desc.SampleDesc.Count = 1;
tex_desc.SampleDesc.Quality = 0;
tex_desc.Usage = D3D11_USAGE_DEFAULT;
+117 -131
View File
@@ -6287,15 +6287,23 @@ struct d3d_gl_fbo_tex_struct {
GLuint tex;
HANDLE handle;
inline d3d_gl_fbo_tex_struct() : tex(), handle() {
inline d3d_gl_fbo_tex_struct() : fbo(), tex(), handle() {
}
inline void init(ID3D11Texture2D* texture) {
inline void init(ID3D11Texture2D* texture = 0) {
glGenTextures(1, &tex);
handle = wglDXRegisterObjectNV(d3d_gl_handle, texture,
tex, GL_TEXTURE_2D, WGL_ACCESS_READ_WRITE_NV);
if (GLAD_WGL_NV_DX_interop2)
handle = wglDXRegisterObjectNV(d3d_gl_handle, texture,
tex, GL_TEXTURE_2D, WGL_ACCESS_READ_WRITE_NV);
else {
gl_state_bind_texture_2d(tex);
::texture* tex = rctx_ptr->screen_buffer.color_texture;
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8,
tex->width, tex->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
gl_state_bind_texture_2d(0);
}
glGenFramebuffers(1, &fbo);
@@ -6310,19 +6318,23 @@ struct d3d_gl_fbo_tex_struct {
glDeleteFramebuffers(1, &fbo);
fbo = 0;
wglDXUnregisterObjectNV(d3d_gl_handle, handle);
handle = 0;
if (GLAD_WGL_NV_DX_interop2 && handle) {
wglDXUnregisterObjectNV(d3d_gl_handle, handle);
handle = 0;
}
glDeleteTextures(1, &tex);
tex = 0;
}
inline void lock() {
wglDXLockObjectsNV(d3d_gl_handle, 1, &handle);
if (GLAD_WGL_NV_DX_interop2 && handle)
wglDXLockObjectsNV(d3d_gl_handle, 1, &handle);
}
inline void unlock() {
wglDXUnlockObjectsNV(d3d_gl_handle, 1, &handle);
if (GLAD_WGL_NV_DX_interop2 && handle)
wglDXUnlockObjectsNV(d3d_gl_handle, 1, &handle);
}
};
@@ -6339,8 +6351,8 @@ waitable_timer d3d_timer;
static const size_t nvenc_src_pixel_size = 4;
static const size_t nvenc_dst_pixel_size = 4;
static nvenc_encoder_format nvenc_format;
static bool nvenc_lossless;
static nvenc_encoder_format nvenc_format = NVENC_ENCODER_YUV420_10BIT;
static bool nvenc_lossless = false;
static bool nvenc_rgb;
@@ -6393,64 +6405,71 @@ bool x_pv_game::ctrl() {
#if BAKE_PNG || BAKE_VIDEO
if (img_write && frame_prev != x_pv_game_ptr->frame) {
texture* tex = rctx_ptr->screen_buffer.color_texture;
int32_t width = tex->width;
int32_t height = tex->height;
const int32_t width = tex->width;
const int32_t height = tex->height;
#if BAKE_VIDEO
if (GLAD_WGL_NV_DX_interop2) {
d3d_gl_fbo_tex.lock();
d3d_gl_fbo_tex.lock();
gl_state_bind_framebuffer(d3d_gl_fbo_tex.fbo);
gl_state_active_bind_texture_2d(0, tex->glid);
gl_state_bind_sampler(0, rctx_ptr->render_samplers[1]);
if (nvenc_rgb) {
GLint swizzle_rgb1[] = { GL_RED, GL_BLUE, GL_GREEN, GL_ONE };
glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, swizzle_rgb1);
}
gl_state_set_viewport(0, 0, width, height);
uniform_value[U_REDUCE] = 0;
shaders_ft.set(SHADER_FT_REDUCE);
rctx_ptr->render.draw_quad(width, height,
1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f);
if (nvenc_rgb) {
GLint swizzle_rgba[] = { GL_RED, GL_GREEN, GL_BLUE, GL_ALPHA };
glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, swizzle_rgba);
}
gl_state_bind_sampler(0, 0);
gl_state_bind_texture_2d(0);
gl_state_bind_framebuffer(0);
d3d_gl_fbo_tex.unlock();
gl_state_bind_framebuffer(d3d_gl_fbo_tex.fbo);
gl_state_active_bind_texture_2d(0, tex->glid);
gl_state_bind_sampler(0, rctx_ptr->render_samplers[1]);
#if BAKE_VIDEO_ALPHA
d3d_gl_alpha_fbo_tex.lock();
GLint swizzle_rgb1[] = { GL_RED, GL_GREEN, GL_BLUE, GL_ONE };
glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, swizzle_rgb1);
#endif
gl_state_bind_framebuffer(d3d_gl_alpha_fbo_tex.fbo);
gl_state_active_bind_texture_2d(0, tex->tex);
gl_state_set_viewport(0, 0, width, height);
if (nvenc_rgb)
shaders_dev.set(SHADER_DEV_CONVERT_RGB);
else
shaders_dev.set(SHADER_DEV_CONVERT_YCBCR_BT709);
rctx_ptr->render.draw_quad(width, height,
1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f);
#if BAKE_VIDEO_ALPHA
GLint swizzle_rgba[] = { GL_RED, GL_GREEN, GL_BLUE, GL_ALPHA };
glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, swizzle_rgba);
#endif
gl_state_bind_sampler(0, 0);
gl_state_bind_texture_2d(0);
gl_state_bind_framebuffer(0);
d3d_gl_fbo_tex.unlock();
#if BAKE_VIDEO_ALPHA
d3d_gl_alpha_fbo_tex.lock();
gl_state_bind_framebuffer(d3d_gl_alpha_fbo_tex.fbo);
gl_state_active_bind_texture_2d(0, tex->glid);
if (nvenc_rgb) {
GLint swizzle_aaa1[] = { GL_ALPHA, GL_ALPHA, GL_ALPHA, GL_ONE };
glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, swizzle_aaa1);
}
gl_state_set_viewport(0, 0, width, height);
gl_state_set_viewport(0, 0, width, height);
if (nvenc_rgb) {
uniform_value[U_REDUCE] = 0;
shaders_ft.set(SHADER_FT_REDUCE);
rctx_ptr->render.draw_quad(width, height,
1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f);
}
else
shaders_dev.set(SHADER_DEV_CONVERT_ALPHA);
rctx_ptr->render.draw_quad(width, height,
1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f);
GLint swizzle_rgba[] = { GL_RED, GL_GREEN, GL_BLUE, GL_ALPHA };
if (nvenc_rgb)
glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, swizzle_rgba);
gl_state_bind_texture_2d(0);
gl_state_bind_framebuffer(0);
gl_state_bind_texture_2d(0);
gl_state_bind_framebuffer(0);
d3d_gl_alpha_fbo_tex.unlock();
#endif
}
else {
d3d_gl_alpha_fbo_tex.unlock();
#endif
if (!GLAD_WGL_NV_DX_interop2) {
void (*conv_func)(uint8_t * src, uint8_t * dst);
conv_func = [](uint8_t* src, uint8_t* dst) {
@@ -6458,71 +6477,10 @@ bool x_pv_game::ctrl() {
*(uint32_t*)dst = (temp & 0xFFFFFF) | (0xFF << 24);
};
if (nvenc_rgb) {
conv_func = [](uint8_t* src, uint8_t* dst) {
const uint32_t temp = *(uint32_t*)src;
*(uint32_t*)dst = (temp & 0xFF) | ((temp & 0xFF00) << 8)
| ((temp & 0xFF0000) >> 8) | (0xFF << 24);
};
}
#if BAKE_VIDEO_ALPHA
void (*conv_func_alpha)(uint8_t * src, uint8_t * dst);
conv_func_alpha = [](uint8_t* src, uint8_t* dst) {
const uint32_t temp = *(uint32_t*)src;
const uint32_t alpha = (temp >> 24) & 0xFF;
*(uint32_t*)dst = alpha | (alpha << 8) | (alpha << 16) | (0xFF << 24);
};
if (nvenc_rgb) {
conv_func_alpha = [](uint8_t* src, uint8_t* dst) {
const uint32_t temp = *(uint32_t*)src;
const uint32_t alpha = (temp >> 24) & 0xFF;
*(uint32_t*)dst = alpha | (alpha << 8) | (alpha << 16) | (0xFF << 24);
};
}
D3D11_MAPPED_SUBRESOURCE mapped_res = {};
HRESULT result = d3d_device_context->Map(d3d_texture,
0, D3D11_MAP_WRITE_DISCARD, 0, &mapped_res);
D3D11_MAPPED_SUBRESOURCE mapped_alpha_res = {};
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->tex);
glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_HALF_FLOAT, nvenc_temp_pixels.data());
gl_state_bind_texture_2d(0);
uint8_t* src = (uint8_t*)nvenc_temp_pixels.data();
uint8_t* dst = (uint8_t*)mapped_res.pData;
uint8_t* dst_alpha = (uint8_t*)mapped_alpha_res.pData;
for (size_t y = 0, i = height; i; y++, i--) {
uint8_t* src1 = &src[y * width * nvenc_src_pixel_size];
uint8_t* dst1 = &dst[(height - y - 1) * width * nvenc_dst_pixel_size];
uint8_t* dst1_alpha = &dst_alpha[(height - y - 1) * width * nvenc_dst_pixel_size];
for (size_t x = 0, j = width; j; x++, j--, src1 += nvenc_src_pixel_size,
dst1 += nvenc_dst_pixel_size, dst1_alpha += nvenc_dst_pixel_size) {
conv_func(src1, dst1);
conv_func_alpha(src1, dst1_alpha);
}
}
d3d_device_context->Unmap(d3d_texture, 0);
d3d_device_context->Unmap(d3d_alpha_texture, 0);
}
else {
if (SUCCEEDED(result))
d3d_device_context->Unmap(d3d_texture, 0);
if (SUCCEEDED(result_alpha))
d3d_device_context->Unmap(d3d_alpha_texture, 0);
}
#else
D3D11_MAPPED_SUBRESOURCE mapped_res = {};
if (SUCCEEDED(d3d_device_context->Map(d3d_texture, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped_res))) {
gl_state_bind_texture_2d(tex->glid);
if (SUCCEEDED(d3d_device_context->Map(d3d_texture,
0, D3D11_MAP_WRITE_DISCARD, 0, &mapped_res))) {
gl_state_bind_texture_2d(d3d_gl_fbo_tex.tex);
glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, nvenc_temp_pixels.data());
gl_state_bind_texture_2d(0);
@@ -6540,6 +6498,29 @@ bool x_pv_game::ctrl() {
d3d_device_context->Unmap(d3d_texture, 0);
}
#if BAKE_VIDEO_ALPHA
D3D11_MAPPED_SUBRESOURCE mapped_alpha_res = {};
if (SUCCEEDED(d3d_device_context->Map(d3d_alpha_texture,
0, D3D11_MAP_WRITE_DISCARD, 0, &mapped_alpha_res))) {
gl_state_bind_texture_2d(d3d_gl_alpha_fbo_tex.tex);
glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, nvenc_temp_pixels.data());
gl_state_bind_texture_2d(0);
uint8_t* src = (uint8_t*)nvenc_temp_pixels.data();
uint8_t* dst = (uint8_t*)mapped_alpha_res.pData;
for (size_t y = 0, i = height; i; y++, i--) {
uint8_t* src1 = &src[y * width * nvenc_src_pixel_size];
uint8_t* dst1 = &dst[(height - y - 1) * width * nvenc_dst_pixel_size];
for (size_t x = 0, j = width; j; x++, j--,
src1 += nvenc_src_pixel_size, dst1 += nvenc_dst_pixel_size)
conv_func(src1, dst1);
}
d3d_device_context->Unmap(d3d_alpha_texture, 0);
}
#endif
}
@@ -7899,8 +7880,7 @@ bool x_pv_game::ctrl() {
const int32_t height = BAKE_BASE_HEIGHT * BAKE_RES_SCALE;
#if BAKE_VIDEO
nvenc_rgb = (nvenc_format == NVENC_ENCODER_RGB
|| nvenc_format == NVENC_ENCODER_RGB_10BIT);
nvenc_rgb = nvenc_format == NVENC_ENCODER_RGB || nvenc_format == NVENC_ENCODER_RGB_10BIT;
{
D3D11_TEXTURE2D_DESC tex_desc = {};
@@ -7908,7 +7888,7 @@ bool x_pv_game::ctrl() {
tex_desc.Height = height;
tex_desc.MipLevels = 1;
tex_desc.ArraySize = 1;
tex_desc.Format = nvenc_rgb ? DXGI_FORMAT_AYUV : DXGI_FORMAT_R8G8B8A8_UNORM;
tex_desc.Format = DXGI_FORMAT_AYUV;
tex_desc.SampleDesc.Count = 1;
tex_desc.SampleDesc.Quality = 0;
if (GLAD_WGL_NV_DX_interop2) {
@@ -7929,12 +7909,10 @@ bool x_pv_game::ctrl() {
#endif
}
if (GLAD_WGL_NV_DX_interop2) {
d3d_gl_fbo_tex.init(d3d_texture);
d3d_gl_fbo_tex.init(d3d_texture);
#if BAKE_VIDEO_ALPHA
d3d_gl_alpha_fbo_tex.init(d3d_alpha_texture);
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);
@@ -8035,12 +8013,10 @@ bool x_pv_game::ctrl() {
delete nvenc_enc;
nvenc_enc = 0;
if (GLAD_WGL_NV_DX_interop2) {
#if BAKE_VIDEO_ALPHA
d3d_gl_alpha_fbo_tex.free();
d3d_gl_alpha_fbo_tex.free();
#endif
d3d_gl_fbo_tex.free();
}
d3d_gl_fbo_tex.free();
#if BAKE_VIDEO_ALPHA
d3d_alpha_texture->Release();
@@ -10261,10 +10237,6 @@ XPVGameSelector::~XPVGameSelector() {
bool XPVGameSelector::init() {
start = false;
exit = false;
#if BAKE_VIDEO
nvenc_format = NVENC_ENCODER_YUV420_10BIT;
nvenc_lossless = false;
#endif
return true;
}
@@ -10469,6 +10441,20 @@ void XPVGameSelector::window() {
};
ImGui::ColumnComboBox("Format", nvenc_format_names, 6, (int32_t*)&nvenc_format, 0, false, &focus);
switch (nvenc_format) {
case NVENC_ENCODER_YUV420:
case NVENC_ENCODER_YUV444:
case NVENC_ENCODER_RGB:
ImGui::Checkbox("Lossless", &nvenc_lossless);
break;
case NVENC_ENCODER_YUV420_10BIT:
case NVENC_ENCODER_YUV444_10BIT:
case NVENC_ENCODER_RGB_10BIT:
ImGui::Checkbox("Lossless (Will be saved at 8 bits!)", &nvenc_lossless);
break;
}
ImGui::Checkbox("Lossless", &nvenc_lossless);
ImGui::EndChild();
}