载入中...
搜索中...
未找到
GraphicsDisplayOutput.cpp
浏览该文件的文档.
4
5#include <cmath>
6#include <optional>
7#include <vector>
8
9namespace eve::graphics::vulkan {
10namespace {
11
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))
20 support.hdr10 = true;
21 if (fmt.colorSpace == vk::ColorSpaceKHR::eExtendedSrgbLinearEXT &&
22 (fmt.format == vk::Format::eR16G16B16A16Sfloat ||
23 fmt.format == vk::Format::eB10G11R11UfloatPack32))
24 support.scRgb = true;
25 }
26 return support;
27}
28
29vk::SurfaceFormatKHR chooseSurfaceFormat(const std::vector<vk::SurfaceFormatKHR> &available,
31 Graphics::DisplayColorSpace &activeOut) {
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;
35 return false;
36 };
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};
43 }
44 return std::nullopt;
45 };
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};
52 }
53 return std::nullopt;
54 };
55
56 std::optional<vk::SurfaceFormatKHR> chosen;
58 switch (mode) {
60 chosen = pickHdr10();
61 break;
63 chosen = pickScRgb();
64 break;
66 chosen = pickHdr10();
67 if (!chosen) chosen = pickScRgb();
68 break;
70 default:
71 break;
72 }
73 if (chosen) {
74 activeOut = chosen->colorSpace == vk::ColorSpaceKHR::eHdr10St2084EXT
77 return *chosen;
78 }
79
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},
85 };
86 for (const auto &pref : sdrPrefs) {
87 if (has(pref.format, pref.colorSpace)) return pref;
88 }
89 if (!available.empty()) return available.front();
90 return sdrPrefs[0];
91}
92
93} // namespace
94
96 if (mode != DisplayOutputMode::Sdr && mode != DisplayOutputMode::Auto &&
99 "Unknown display output mode",
100 "graphics.presentation"));
101 if (mode == displayOutputMode_) return Result<void>::success();
102 displayOutputMode_ = mode;
103 markSwapchainDirty();
104 return Result<void>::success();
105}
106
107Result<void> Graphics::setDisplayHdrCalibration(float paperWhiteNits, float peakNits) {
108 if (!std::isfinite(paperWhiteNits) || !std::isfinite(peakNits))
110 "Display HDR calibration must be finite",
111 "graphics.presentation"));
112 display::clampCalibration(paperWhiteNits, peakNits);
113 displayPaperWhiteNits_ = paperWhiteNits;
114 displayPeakNits_ = peakNits;
115 return Result<void>::success();
116}
117
119 if (!initialized || !surface)
121 DiagnosticCode::Failed, "Display output support requires an initialized window surface",
122 "graphics.presentation"));
124 probeSupport(device.physical_device, surface));
125}
126
127void Graphics::applyPreferredSwapchainFormats(vkb::SwapchainBuilder &builder) {
129 std::vector<vk::SurfaceFormatKHR> available;
130 if (surface)
131 available = device.physical_device->getSurfaceFormatsKHR(surface);
132 selectedSurfaceFormat_ = chooseSurfaceFormat(available, displayOutputMode_, active);
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});
139}
140
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{};
148 }
149 destroySampler(device, slot.colorGpu.sampler);
150 }
151 presentComposeSlots.clear();
152 presentComposeWidth = 0;
153 presentComposeHeight = 0;
154}
155
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)
162 return;
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);
172 slot.framebuffer =
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)
179 .build(device);
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;
193 }
194}
195
196Graphics::PresentComposeSlot *Graphics::currentPresentComposeSlot() {
197 if (presentComposeSlots.empty()) return nullptr;
198 return &presentComposeSlots[currentFrameSlot() % presentComposeSlots.size()];
199}
200
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;
218 return true;
219}
220
221void Graphics::endPresentComposePass() {
222 if (!presentComposeActive_) return;
223 auto &cb = currentPresentCb();
224 cb.endRenderPass();
225 if (auto *slot = currentPresentComposeSlot()) slot->color.endSampledLayout();
226 presentComposeActive_ = false;
227}
228
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);
233 const display::ActiveColorSpace space =
237 const Color tint = display::sceneResolveTint(false, space, false, displayPaperWhiteNits_,
239 Batcher batch;
240 batch.addTexturedRect(0.f, 0.f, float(width), float(height), tint, 0.f, 0.f, 1.f, 1.f);
241 batch.toNDC(width, height);
242 std::vector<TexturedVertex> gpuVerts;
243 gpuVerts.reserve(batch.vertices().size());
244 for (const auto &v : batch.vertices())
245 gpuVerts.push_back(TexturedVertex{v.pos, v.color, v.uv});
246 auto &frameBufs = currentFrame2DBuffers();
247 if (frameBufs.texBufs.empty()) frameBufs.texBufs.emplace_back();
248 // Append a dedicated vertex buffer so the encode blit does not overwrite
249 // buffers still referenced by the just-recorded compose draws.
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,
256 nullptr);
257 vk::DeviceSize offset = 0;
258 cb.bindVertexBuffers(0, 1, vb, &offset);
259 cb.draw(uint32_t(gpuVerts.size()), 1, 0, 0);
260}
261
262} // namespace eve::graphics::vulkan
bool & active
float w
Definition AnimClip.cpp:738
std::string colorSpace
float uv
glm::vec4 tint
float v
int h
size_t offset
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.
Definition Diagnostic.h:125
Move-only operation result carrying either a value or Status.
Definition Result.h:155
static Result success(T value)
Construct a successful result owning value.
Definition Result.h:164
static Result failure(Status status)
Construct a failed result from a structured status.
Definition Result.h:175
DisplayOutputMode
Requested swapchain present preference.
Definition Graphics.h:1327
DisplayColorSpace activeDisplayColorSpace_
Definition Graphics.h:2254
DisplayColorSpace
Color space actually selected for the live swapchain.
Definition Graphics.h:1329
DisplayOutputMode displayOutputMode_
Definition Graphics.h:2253
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...
Definition Color.h:13
glm::vec4 color