12Graphics::DisplayOutputSupport probeSupport(vk::PhysicalDevice phys, vk::SurfaceKHR surface) {
13 Graphics::DisplayOutputSupport support{};
14 if (!phys || !surface)
return support;
15 const auto formats = phys.getSurfaceFormatsKHR(surface);
16 for (
const auto &fmt : formats) {
17 if (fmt.colorSpace == vk::ColorSpaceKHR::eHdr10St2084EXT &&
18 (fmt.format == vk::Format::eA2B10G10R10UnormPack32 ||
19 fmt.format == vk::Format::eA2R10G10B10UnormPack32))
21 if (fmt.colorSpace == vk::ColorSpaceKHR::eExtendedSrgbLinearEXT &&
22 (fmt.format == vk::Format::eR16G16B16A16Sfloat ||
23 fmt.format == vk::Format::eB10G11R11UfloatPack32))
29vk::SurfaceFormatKHR chooseSurfaceFormat(
const std::vector<vk::SurfaceFormatKHR> &available,
32 auto has = [&](vk::Format format, vk::ColorSpaceKHR space) {
33 for (
const auto &fmt : available)
34 if (fmt.format == format && fmt.
colorSpace == space) return true;
37 auto pickHdr10 = [&]() -> std::optional<vk::SurfaceFormatKHR> {
38 const vk::Format formats[] = {vk::Format::eA2B10G10R10UnormPack32,
39 vk::Format::eA2R10G10B10UnormPack32};
40 for (vk::Format format : formats) {
41 if (has(format, vk::ColorSpaceKHR::eHdr10St2084EXT))
42 return vk::SurfaceFormatKHR{format, vk::ColorSpaceKHR::eHdr10St2084EXT};
46 auto pickScRgb = [&]() -> std::optional<vk::SurfaceFormatKHR> {
47 const vk::Format formats[] = {vk::Format::eR16G16B16A16Sfloat,
48 vk::Format::eB10G11R11UfloatPack32};
49 for (vk::Format format : formats) {
50 if (has(format, vk::ColorSpaceKHR::eExtendedSrgbLinearEXT))
51 return vk::SurfaceFormatKHR{format, vk::ColorSpaceKHR::eExtendedSrgbLinearEXT};
56 std::optional<vk::SurfaceFormatKHR> chosen;
67 if (!chosen) chosen = pickScRgb();
74 activeOut = chosen->colorSpace == vk::ColorSpaceKHR::eHdr10St2084EXT
80 const vk::SurfaceFormatKHR sdrPrefs[] = {
81 {vk::Format::eB8G8R8A8Unorm, vk::ColorSpaceKHR::eSrgbNonlinear},
82 {vk::Format::eR8G8B8A8Unorm, vk::ColorSpaceKHR::eSrgbNonlinear},
83 {vk::Format::eB8G8R8A8Srgb, vk::ColorSpaceKHR::eSrgbNonlinear},
84 {vk::Format::eR8G8B8A8Srgb, vk::ColorSpaceKHR::eSrgbNonlinear},
86 for (
const auto &pref : sdrPrefs) {
87 if (has(pref.format, pref.colorSpace))
return pref;
89 if (!available.empty())
return available.front();
99 "Unknown display output mode",
100 "graphics.presentation"));
103 markSwapchainDirty();
108 if (!std::isfinite(paperWhiteNits) || !std::isfinite(peakNits))
110 "Display HDR calibration must be finite",
111 "graphics.presentation"));
119 if (!initialized || !surface)
122 "graphics.presentation"));
124 probeSupport(device.physical_device, surface));
127void Graphics::applyPreferredSwapchainFormats(vkb::SwapchainBuilder &builder) {
129 std::vector<vk::SurfaceFormatKHR> available;
131 available = device.physical_device->getSurfaceFormatsKHR(surface);
134 builder.set_desired_format(selectedSurfaceFormat_);
135 builder.add_fallback_format({vk::Format::eB8G8R8A8Unorm, vk::ColorSpaceKHR::eSrgbNonlinear});
136 builder.add_fallback_format({vk::Format::eR8G8B8A8Unorm, vk::ColorSpaceKHR::eSrgbNonlinear});
137 builder.add_fallback_format({vk::Format::eB8G8R8A8Srgb, vk::ColorSpaceKHR::eSrgbNonlinear});
138 builder.add_fallback_format({vk::Format::eR8G8B8A8Srgb, vk::ColorSpaceKHR::eSrgbNonlinear});
141void Graphics::destroyPresentComposeResources() {
142 if (device.instance) device->waitIdle();
143 for (
auto &slot : presentComposeSlots) {
144 slot.colorTex.gpuHandle =
nullptr;
145 if (slot.framebuffer) {
146 device->destroyFramebuffer(slot.framebuffer);
147 slot.framebuffer = vk::Framebuffer{};
149 destroySampler(device, slot.colorGpu.sampler);
151 presentComposeSlots.clear();
152 presentComposeWidth = 0;
153 presentComposeHeight = 0;
156void Graphics::ensurePresentComposeResources(
int composeW,
int composeH) {
157 if (composeW <= 0 || composeH <= 0)
return;
158 ensureHdrOffscreenPipelines();
159 if (!texSetLayout || !descriptorPool || !hdrOffscreenRenderPass)
return;
160 if (!presentComposeSlots.empty() && presentComposeWidth == composeW &&
161 presentComposeHeight == composeH)
163 destroyPresentComposeResources();
164 presentComposeWidth = composeW;
165 presentComposeHeight = composeH;
166 const uint32_t
w = uint32_t(composeW);
167 const uint32_t
h = uint32_t(composeH);
168 const vk::Format colorFmt = vk::Format::eR16G16B16A16Sfloat;
169 presentComposeSlots.resize(kAsyncResourceCopies);
170 for (
auto &slot : presentComposeSlots) {
171 slot.color = device.createColorTarget(
w,
h, colorFmt);
173 hdrOffscreenRenderPass.createFramebuffer(device,
w,
h, {slot.color.asAttachment()});
174 vkb::SamplerBuilder sb;
175 slot.colorGpu.sampler = sb.magFilter(vk::Filter::eLinear)
176 .minFilter(vk::Filter::eLinear)
177 .addressModeU(vk::SamplerAddressMode::eClampToEdge)
178 .addressModeV(vk::SamplerAddressMode::eClampToEdge)
180 auto sets = vkb::DescriptorSetBuilder()
181 .layout(texSetLayout)
182 .build(device.instance, descriptorPool);
183 slot.colorGpu.descriptorSet = vkb::BoundSet{sets[0]};
184 slot.colorGpu.width = composeW;
185 slot.colorGpu.height = composeH;
186 slot.colorGpu.viewOverride = slot.color.imageView();
187 writeCombinedImageDescriptor(&slot.colorGpu);
188 slot.colorTex.width = composeW;
189 slot.colorTex.height = composeH;
190 slot.colorTex.pixelWidth = composeW;
191 slot.colorTex.pixelHeight = composeH;
192 slot.colorTex.gpuHandle = &slot.colorGpu;
196Graphics::PresentComposeSlot *Graphics::currentPresentComposeSlot() {
197 if (presentComposeSlots.empty())
return nullptr;
198 return &presentComposeSlots[currentFrameSlot() % presentComposeSlots.size()];
201bool Graphics::beginPresentComposePass() {
202 ensurePresentComposeResources(
int(swapchain.extent.width),
int(swapchain.extent.height));
203 auto *slot = currentPresentComposeSlot();
204 if (!slot || !hdrOffscreenRenderPass || !slot->framebuffer)
return false;
205 auto &cb = currentPresentCb();
206 vk::ClearValue
clear{};
207 clear.color = vk::ClearColorValue(
208 std::array<float, 4>{clearColor.r, clearColor.g, clearColor.b, clearColor.a});
209 vk::RenderPassBeginInfo rpBegin{};
210 rpBegin.renderPass = hdrOffscreenRenderPass;
211 rpBegin.framebuffer = slot->framebuffer;
212 rpBegin.renderArea = vk::Rect2D{{0, 0}, {uint32_t(presentComposeWidth), uint32_t(presentComposeHeight)}};
213 rpBegin.clearValueCount = 1;
214 rpBegin.pClearValues = &
clear;
215 slot->color.beginColorAttachment();
216 cb.beginRenderPass(rpBegin, vk::SubpassContents::eInline);
217 presentComposeActive_ =
true;
221void Graphics::endPresentComposePass() {
222 if (!presentComposeActive_)
return;
223 auto &cb = currentPresentCb();
225 if (
auto *slot = currentPresentComposeSlot()) slot->color.endSampledLayout();
226 presentComposeActive_ =
false;
229void Graphics::encodePresentComposeToSwapchain(vk::CommandBuffer cb) {
230 auto *slot = currentPresentComposeSlot();
231 if (!slot || !slot->colorTex.gpuHandle || !sceneTonemapPipeline)
return;
232 auto *gpu =
static_cast<GpuTexture *
>(slot->colorTex.gpuHandle);
240 batch.addTexturedRect(0.f, 0.f,
float(
width),
float(
height),
tint, 0.f, 0.f, 1.f, 1.f);
242 std::vector<TexturedVertex> gpuVerts;
243 gpuVerts.reserve(batch.vertices().size());
246 auto &frameBufs = currentFrame2DBuffers();
247 if (frameBufs.texBufs.empty()) frameBufs.texBufs.emplace_back();
250 frameBufs.texBufs.emplace_back();
251 vkb::HostVertexBuffer &vb = frameBufs.texBufs.back();
252 vb.allocate<TexturedVertex>(frameToken(), device, gpuVerts);
253 cb.bindPipeline(vk::PipelineBindPoint::eGraphics, sceneTonemapPipeline);
254 vk::DescriptorSet texSet = gpu->descriptorSet;
255 cb.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, texPipelineLayout, 0, 1, &texSet, 0,
257 vk::DeviceSize
offset = 0;
258 cb.bindVertexBuffers(0, 1, vb, &
offset);
259 cb.draw(uint32_t(gpuVerts.size()), 1, 0, 0);
std::vector< Point > vertices
static Diagnostic error(DiagnosticCode code, std::string message, std::string path={}, DiagnosticDetails details={}, std::string source={})
Construct an error diagnostic with the standard error severity.
Move-only operation result carrying either a value or Status.
static Result success(T value)
Construct a successful result owning value.
static Result failure(Status status)
Construct a failed result from a structured status.
float displayPaperWhiteNits_
DisplayOutputMode
Requested swapchain present preference.
DisplayColorSpace activeDisplayColorSpace_
DisplayColorSpace
Color space actually selected for the live swapchain.
DisplayOutputMode displayOutputMode_
Result< void > setDisplayOutputMode(DisplayOutputMode mode) override
Sets the display output mode.
Result< DisplayOutputSupport > queryDisplayOutputSupport() const override
Queries display output support.
Result< void > setDisplayHdrCalibration(float paperWhiteNits, float peakNits) override
Sets the display hdr calibration.
void clear(std::optional< Color > color, std::optional< int > stencil, std::optional< double > depth) override
Clears .
Color sceneResolveTint(bool aces, ActiveColorSpace space, bool attachmentEncodesSrgb, float paperWhiteNits, float peakNits) noexcept
Build the Color tint used by the final scene present resolve.
void clampCalibration(float &paperWhiteNits, float &peakNits) noexcept
Clamp paper-white / peak luminance into the engine-supported range.
ActiveColorSpace
Active present color encoding after swapchain selection.
eve::Color Color
RGBA color used by every graphics draw call. Lives inside eve::graphics so including a graphics heade...