Files
bgfx/examples/55-hdr10/hdr10.cpp

310 lines
7.8 KiB
C++

/*
* Copyright 2011-2026 Branimir Karadzic. All rights reserved.
* License: https://github.com/bkaradzic/bgfx/blob/master/LICENSE
*/
#include "common.h"
#include "bgfx_utils.h"
#include "imgui/imgui.h"
#include <bx/math.h>
namespace
{
constexpr uint32_t kSwapChainFlags = 0
| BGFX_SWAP_CHAIN_FULLSCREEN_MASK
| BGFX_SWAP_CHAIN_MSAA_MASK
| BGFX_SWAP_CHAIN_SRGB_BACKBUFFER
| BGFX_SWAP_CHAIN_HDR10
| BGFX_SWAP_CHAIN_HIDPI
| BGFX_SWAP_CHAIN_TRANSPARENT_BACKBUFFER
;
constexpr uint32_t kNumGray = 7;
constexpr uint32_t kNumBars = 6;
constexpr float kGrayNits[kNumGray] = { 0.0f, 100.0f, 203.0f, 400.0f, 1000.0f, 4000.0f, 10000.0f };
constexpr float kGrayScale[kNumGray] = { 0.0f, 0.5f, 1.0f, 2.0f, 4.0f, 8.0f, 16.0f };
constexpr float kBarNits = 203.0f;
constexpr float kBarColor[kNumBars][3] =
{
{ 1.0f, 0.0f, 0.0f },
{ 0.0f, 1.0f, 0.0f },
{ 0.0f, 0.0f, 1.0f },
{ 0.0f, 1.0f, 1.0f },
{ 1.0f, 0.0f, 1.0f },
{ 1.0f, 1.0f, 0.0f },
};
constexpr const char* kBarName[kNumBars] = { "R", "G", "B", "C", "M", "Y" };
float encodePq(float _nits)
{
constexpr float m1 = 2610.0f/16384.0f;
constexpr float m2 = 2523.0f/4096.0f*128.0f;
constexpr float c1 = 3424.0f/4096.0f;
constexpr float c2 = 2413.0f/4096.0f*32.0f;
constexpr float c3 = 2392.0f/4096.0f*32.0f;
const float yy = bx::pow(bx::clamp(_nits/10000.0f, 0.0f, 1.0f), m1);
return bx::pow( (c1 + c2*yy) / (1.0f + c3*yy), m2);
}
class ExampleHdr10 : public entry::AppI
{
public:
ExampleHdr10(const char* _name, const char* _description, const char* _url)
: entry::AppI(_name, _description, _url)
{
}
void init(int32_t _argc, const char* const* _argv, uint32_t _width, uint32_t _height) override
{
Args args(_argc, _argv);
m_width = _width;
m_height = _height;
m_debug = BGFX_DEBUG_NONE;
m_reset = BGFX_RESET_VSYNC|BGFX_RESET_HDR10;
m_pq = true;
bgfx::Init init;
init.type = args.m_type;
init.vendorId = args.m_pciId;
init.swapChain.nwh = entry::getNativeWindowHandle(entry::kDefaultWindowHandle);
init.swapChain.ndt = entry::getNativeDisplayHandle();
init.platformData.type = entry::getNativeWindowHandleType();
init.swapChain.width = m_width;
init.swapChain.height = m_height;
init.swapChain.formatColor = getFormat();
init.reset = m_reset;
bgfx::init(init);
bgfx::setDebug(m_debug);
m_appliedWidth = m_width;
m_appliedHeight = m_height;
m_appliedReset = m_reset;
m_appliedPq = m_pq;
imguiCreate();
}
int shutdown() override
{
imguiDestroy();
bgfx::shutdown();
return 0;
}
bgfx::TextureFormat::Enum getFormat() const
{
return m_pq
? bgfx::TextureFormat::RGB10A2
: bgfx::TextureFormat::RGBA16F
;
}
void setPatch(uint8_t _index, float _red, float _green, float _blue)
{
const float rgba[4] = { _red, _green, _blue, 1.0f };
bgfx::setPaletteColor(_index, rgba);
}
float encode(float _nits, float _scale) const
{
return m_pq ? encodePq(_nits) : _scale;
}
void imguiLabel(float _x, float _y, const char* _id, const char* _text)
{
ImGui::SetNextWindowPos(ImVec2(_x, _y), 0, ImVec2(0.5f, 1.0f) );
ImGui::SetNextWindowBgAlpha(0.5f);
ImGui::Begin(_id, NULL, 0
| ImGuiWindowFlags_NoInputs
| ImGuiWindowFlags_NoTitleBar
| ImGuiWindowFlags_NoMove
| ImGuiWindowFlags_NoResize
| ImGuiWindowFlags_NoSavedSettings
| ImGuiWindowFlags_AlwaysAutoResize
);
ImGui::Text("%s", _text);
ImGui::End();
}
void submitPatch(bgfx::ViewId _view, uint8_t _index, uint16_t _x, uint16_t _y, uint16_t _width, uint16_t _height)
{
bgfx::setViewRect(_view, _x, _y, _width, _height);
bgfx::setViewClear(_view, BGFX_CLEAR_COLOR, 1.0f, 0, _index);
bgfx::touch(_view);
}
bool update() override
{
if (entry::processEvents(m_width, m_height, m_debug, m_reset, &m_mouseState) )
{
return false;
}
imguiBeginFrame(m_mouseState.m_mx
, m_mouseState.m_my
, (m_mouseState.m_buttons[entry::MouseButton::Left ] ? IMGUI_MBUT_LEFT : 0)
| (m_mouseState.m_buttons[entry::MouseButton::Right ] ? IMGUI_MBUT_RIGHT : 0)
| (m_mouseState.m_buttons[entry::MouseButton::Middle] ? IMGUI_MBUT_MIDDLE : 0)
, m_mouseState.m_mz
, uint16_t(m_width)
, uint16_t(m_height)
);
showExampleDialog(this);
bool hdr10 = 0 != (m_reset & BGFX_RESET_HDR10);
ImGui::SetNextWindowPos(ImVec2(m_width - m_width / 5.0f - 10.0f, 10.0f), ImGuiCond_FirstUseEver);
ImGui::SetNextWindowSize(ImVec2(m_width / 5.0f, m_height / 6.0f), ImGuiCond_FirstUseEver);
ImGui::Begin("Settings", NULL, 0);
if (ImGui::RadioButton("PQ, RGB10A2", m_pq) )
{
m_pq = true;
}
if (ImGui::RadioButton("Linear, RGBA16F", !m_pq) )
{
m_pq = false;
}
ImGui::Checkbox("HDR10", &hdr10);
ImGui::End();
const float labelRowHeight = float(m_height*2/5);
char id[32];
char text[128];
for (uint32_t ii = 0; ii < kNumGray; ++ii)
{
if (m_pq)
{
bx::snprintf(text, BX_COUNTOF(text), "%.0f nits", kGrayNits[ii]);
}
else
{
bx::snprintf(text, BX_COUNTOF(text), "%.1fx white", kGrayScale[ii]);
}
bx::snprintf(id, BX_COUNTOF(id), "gray%d", ii);
imguiLabel(m_width*(ii + 0.5f)/kNumGray, labelRowHeight - 10.0f, id, text);
}
for (uint32_t ii = 0; ii < kNumBars; ++ii)
{
if (m_pq)
{
bx::snprintf(text, BX_COUNTOF(text), "%s %.0f nits", kBarName[ii], kBarNits);
}
else
{
bx::snprintf(text, BX_COUNTOF(text), "%s 1.0x white", kBarName[ii]);
}
bx::snprintf(id, BX_COUNTOF(id), "bar%d", ii);
imguiLabel(m_width*(ii + 0.5f)/kNumBars, 2.0f*labelRowHeight - 10.0f, id, text);
}
const bgfx::Caps* caps = bgfx::getCaps();
bx::snprintf(text, BX_COUNTOF(text), "BGFX_CAPS_HDR10: %s, back buffer: %s"
, 0 != (caps->supported & BGFX_CAPS_HDR10) ? "supported" : "not supported"
, m_pq ? "RGB10A2, PQ BT.2020" : "RGBA16F, extended linear sRGB"
);
imguiLabel(m_width*0.5f, m_height - 10.0f, "status", text);
imguiEndFrame();
m_reset = (m_reset & ~BGFX_RESET_HDR10) | (hdr10 ? BGFX_RESET_HDR10 : 0);
if (m_width != m_appliedWidth
|| m_height != m_appliedHeight
|| m_reset != m_appliedReset
|| m_pq != m_appliedPq)
{
m_appliedWidth = m_width;
m_appliedHeight = m_height;
m_appliedReset = m_reset;
m_appliedPq = m_pq;
bgfx::SwapChain swapChain;
swapChain.width = m_width;
swapChain.height = m_height;
swapChain.flags = m_reset & kSwapChainFlags;
swapChain.formatColor = getFormat();
bgfx::reset(m_reset & ~kSwapChainFlags, &swapChain);
}
bgfx::setViewRect(0, 0, 0, uint16_t(m_width), uint16_t(m_height) );
bgfx::setViewClear(0, BGFX_CLEAR_COLOR, 0x000000ff, 1.0f, 0);
bgfx::touch(0);
const uint16_t rowHeight = uint16_t(m_height*2/5);
for (uint32_t ii = 0; ii < kNumGray; ++ii)
{
const float value = encode(kGrayNits[ii], kGrayScale[ii]);
setPatch(uint8_t(ii), value, value, value);
const uint16_t x0 = uint16_t(m_width* ii /kNumGray);
const uint16_t x1 = uint16_t(m_width*(ii + 1)/kNumGray);
submitPatch(bgfx::ViewId(1 + ii), uint8_t(ii), x0, 0, x1 - x0, rowHeight);
}
for (uint32_t ii = 0; ii < kNumBars; ++ii)
{
const float on = encode(kBarNits, 1.0f);
setPatch(uint8_t(kNumGray + ii)
, kBarColor[ii][0]*on
, kBarColor[ii][1]*on
, kBarColor[ii][2]*on
);
const uint16_t x0 = uint16_t(m_width* ii /kNumBars);
const uint16_t x1 = uint16_t(m_width*(ii + 1)/kNumBars);
submitPatch(bgfx::ViewId(1 + kNumGray + ii), uint8_t(kNumGray + ii), x0, rowHeight, x1 - x0, rowHeight);
}
bgfx::frame();
return true;
}
entry::MouseState m_mouseState;
uint32_t m_width;
uint32_t m_height;
uint32_t m_debug;
uint32_t m_reset;
bool m_pq;
uint32_t m_appliedWidth;
uint32_t m_appliedHeight;
uint32_t m_appliedReset;
bool m_appliedPq;
};
} // namespace
ENTRY_IMPLEMENT_MAIN(
ExampleHdr10
, "55-hdr10"
, "HDR10 test pattern."
, "https://bkaradzic.github.io/bgfx/examples.html#hdr10"
);