Blender V5.0
GHOST_XrGraphicsBindingD3D.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2025 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#ifndef _WIN32
10# error "GHOST_XrGraphcisBindingD3D can only be compiled on Windows platforms."
11#endif
12
13#include <algorithm>
14#include <sstream>
15
16#include "GHOST_SystemWin32.hh"
17#include "GHOST_XrException.hh"
19
20static void ghost_format_to_dx_format(GHOST_TXrSwapchainFormat ghost_format,
21 bool expects_srgb_buffer,
22 DXGI_FORMAT &r_dx_format)
23{
24 r_dx_format = DXGI_FORMAT_UNKNOWN;
25
26 switch (ghost_format) {
27 case GHOST_kXrSwapchainFormatRGBA8:
28 r_dx_format = expects_srgb_buffer ? DXGI_FORMAT_R8G8B8A8_UNORM_SRGB :
29 DXGI_FORMAT_R8G8B8A8_UNORM;
30 break;
31 case GHOST_kXrSwapchainFormatRGBA16:
32 r_dx_format = DXGI_FORMAT_R16G16B16A16_UNORM;
33 break;
34 case GHOST_kXrSwapchainFormatRGBA16F:
35 r_dx_format = DXGI_FORMAT_R16G16B16A16_FLOAT;
36 break;
37 case GHOST_kXrSwapchainFormatRGB10_A2:
38 r_dx_format = DXGI_FORMAT_R10G10B10A2_UNORM;
39 break;
40 }
41
42 if (r_dx_format == DXGI_FORMAT_UNKNOWN) {
43 throw GHOST_XrException("No supported DirectX swapchain format found.");
44 }
45}
46
47static std::optional<int64_t> choose_swapchain_format_from_candidates(
48 const std::vector<int64_t> &gpu_binding_formats, const std::vector<int64_t> &runtime_formats)
49{
50 if (gpu_binding_formats.empty()) {
51 return std::nullopt;
52 }
53
54 auto res = std::find_first_of(gpu_binding_formats.begin(),
55 gpu_binding_formats.end(),
56 runtime_formats.begin(),
57 runtime_formats.end());
58 if (res == gpu_binding_formats.end()) {
59 return std::nullopt;
60 }
61
62 return *res;
63}
64
65/* -------------------------------------------------------------------- */
68
79
81 GHOST_Context & /*ghost_ctx*/, /* Remember: This is the OpenGL context! */
82 XrInstance instance,
83 XrSystemId system_id,
84 std::string *r_requirement_info) const
85{
86 static PFN_xrGetD3D11GraphicsRequirementsKHR s_xrGetD3D11GraphicsRequirementsKHR_fn = nullptr;
87 // static XrInstance s_instance = XR_NULL_HANDLE;
88 XrGraphicsRequirementsD3D11KHR gpu_requirements = {XR_TYPE_GRAPHICS_REQUIREMENTS_D3D11_KHR};
89
90 /* Although it would seem reasonable that the PROC address would not change if the instance was
91 * the same, in testing, repeated calls to #xrGetInstanceProcAddress() with the same instance
92 * can still result in changes so the workaround is to simply set the function pointer every
93 * time (trivializing its 'static' designation). */
94 // if (instance != s_instance) {
95 // s_instance = instance;
96 s_xrGetD3D11GraphicsRequirementsKHR_fn = nullptr;
97 //}
98 if (!s_xrGetD3D11GraphicsRequirementsKHR_fn &&
99 XR_FAILED(
100 xrGetInstanceProcAddr(instance,
101 "xrGetD3D11GraphicsRequirementsKHR",
102 (PFN_xrVoidFunction *)&s_xrGetD3D11GraphicsRequirementsKHR_fn)))
103 {
104 s_xrGetD3D11GraphicsRequirementsKHR_fn = nullptr;
105 return false;
106 }
107
108 s_xrGetD3D11GraphicsRequirementsKHR_fn(instance, system_id, &gpu_requirements);
109
110 if (r_requirement_info) {
111 std::ostringstream strstream;
112 strstream << "Minimum DirectX 11 Feature Level " << gpu_requirements.minFeatureLevel
113 << std::endl;
114
115 *r_requirement_info = strstream.str();
116 }
117
118 return ghost_d3d_ctx_->device_->GetFeatureLevel() >= gpu_requirements.minFeatureLevel;
119}
120
122 GHOST_Context & /*ghost_ctx*/ /* Remember: This is the OpenGL context! */,
123 XrInstance /*instance*/,
124 XrSystemId /*system_id*/
125)
126{
127 oxr_binding.d3d11.type = XR_TYPE_GRAPHICS_BINDING_D3D11_KHR;
128 oxr_binding.d3d11.device = ghost_d3d_ctx_->device_;
129}
130
132 const std::vector<int64_t> &runtime_formats,
133 GHOST_TXrSwapchainFormat &r_format,
134 bool &r_is_srgb_format) const
135{
136 std::vector<int64_t> gpu_binding_formats = {
137#if 0 /* RGB10A2, RGBA16 don't seem to work with Oculus head-sets, \
138 * so move them after RGBA16F for the time being. */
139 DXGI_FORMAT_R10G10B10A2_UNORM,
140 DXGI_FORMAT_R16G16B16A16_UNORM,
141#endif
142 DXGI_FORMAT_R16G16B16A16_FLOAT,
143#if 1
144 DXGI_FORMAT_R10G10B10A2_UNORM,
145 DXGI_FORMAT_R16G16B16A16_UNORM,
146#endif
147 DXGI_FORMAT_R8G8B8A8_UNORM,
148 DXGI_FORMAT_R8G8B8A8_UNORM_SRGB,
149 };
150
151 std::optional result = choose_swapchain_format_from_candidates(gpu_binding_formats,
152 runtime_formats);
153 if (result) {
154 switch (*result) {
155 case DXGI_FORMAT_R10G10B10A2_UNORM:
156 r_format = GHOST_kXrSwapchainFormatRGB10_A2;
157 break;
158 case DXGI_FORMAT_R16G16B16A16_UNORM:
159 r_format = GHOST_kXrSwapchainFormatRGBA16;
160 break;
161 case DXGI_FORMAT_R16G16B16A16_FLOAT:
162 r_format = GHOST_kXrSwapchainFormatRGBA16F;
163 break;
164 case DXGI_FORMAT_R8G8B8A8_UNORM:
165 case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB:
166 r_format = GHOST_kXrSwapchainFormatRGBA8;
167 break;
168 }
169 r_is_srgb_format = (*result == DXGI_FORMAT_R8G8B8A8_UNORM_SRGB);
170 }
171 else {
172 r_format = GHOST_kXrSwapchainFormatRGBA8;
173 r_is_srgb_format = false;
174 }
175
176 return result;
177}
178
179std::vector<XrSwapchainImageBaseHeader *> GHOST_XrGraphicsBindingD3D::createSwapchainImages(
180 uint32_t image_count)
181{
182 std::vector<XrSwapchainImageD3D11KHR> d3d_images(image_count);
183 std::vector<XrSwapchainImageBaseHeader *> base_images;
184
185 /* Need to return vector of base header pointers, so of a different type. Need to build a new
186 * list with this type, and keep the initial one alive. */
187 for (XrSwapchainImageD3D11KHR &image : d3d_images) {
188 image.type = XR_TYPE_SWAPCHAIN_IMAGE_D3D11_KHR;
189 base_images.push_back(reinterpret_cast<XrSwapchainImageBaseHeader *>(&image));
190 }
191
192 /* Keep alive. */
193 image_cache_.push_back(std::move(d3d_images));
194
195 return base_images;
196}
197
199{
200 return ghost_d3d_ctx_->isUpsideDown();
201}
202
204
205/* -------------------------------------------------------------------- */
208
214
216{
217 if (shared_resource_) {
218 ghost_d3d_ctx_->disposeSharedOpenGLResource(shared_resource_);
219 shared_resource_ = nullptr;
220 }
221}
222
224 XrSwapchainImageBaseHeader &swapchain_image, const GHOST_XrDrawViewInfo &draw_info)
225{
226 XrSwapchainImageD3D11KHR &d3d_swapchain_image = reinterpret_cast<XrSwapchainImageD3D11KHR &>(
227 swapchain_image);
228
229#if 0
230 /* Ideally we'd just create a render target view for the OpenXR swap-chain image texture and
231 * blit from the OpenGL context into it. The NV_DX_interop extension doesn't want to work with
232 * this though. At least not with OPTIMUS hardware. See:
233 * https://github.com/mpv-player/mpv/issues/2949#issuecomment-197262807.
234 */
235
236 ID3D11RenderTargetView *rtv;
237 CD3D11_RENDER_TARGET_VIEW_DESC rtv_desc(D3D11_RTV_DIMENSION_TEXTURE2D,
238 DXGI_FORMAT_R8G8B8A8_UNORM);
239
240 ghost_ctx_->device_->CreateRenderTargetView(d3d_swapchain_image.texture, &rtv_desc, &rtv);
241 if (!shared_resource_) {
242 DXGI_FORMAT format;
243 ghost_format_to_dx_format(draw_info.swapchain_format, draw_info.expects_srgb_buffer, format);
244 shared_resource_ = ghost_ctx_->createSharedOpenGLResource(
245 draw_info.width, draw_info.height, format, rtv);
246 }
247 ghost_ctx_->blitFromOpenGLContext(shared_resource_, draw_info.width, draw_info.height);
248#else
249 if (!shared_resource_) {
250 DXGI_FORMAT format;
251 ghost_format_to_dx_format(draw_info.swapchain_format, draw_info.expects_srgb_buffer, format);
252 shared_resource_ = ghost_d3d_ctx_->createSharedOpenGLResource(
253 draw_info.width, draw_info.height, format);
254 }
255 ghost_d3d_ctx_->blitFromOpenGLContext(shared_resource_, draw_info.width, draw_info.height);
256
257 ghost_d3d_ctx_->device_ctx_->OMSetRenderTargets(0, nullptr, nullptr);
258 ghost_d3d_ctx_->device_ctx_->CopyResource(d3d_swapchain_image.texture,
259 ghost_d3d_ctx_->getSharedTexture2D(shared_resource_));
260#endif
261}
262
264
265#ifdef WITH_VULKAN_BACKEND
266
267/* -------------------------------------------------------------------- */
270
271GHOST_XrGraphicsBindingVulkanD3D::GHOST_XrGraphicsBindingVulkanD3D(GHOST_Context &ghost_ctx)
272
273 : GHOST_XrGraphicsBindingD3D(), ghost_ctx_(static_cast<GHOST_ContextVK &>(ghost_ctx))
274{
275}
276
277GHOST_XrGraphicsBindingVulkanD3D::~GHOST_XrGraphicsBindingVulkanD3D() {}
278
279void GHOST_XrGraphicsBindingVulkanD3D::submitToSwapchainImage(
280 XrSwapchainImageBaseHeader &swapchain_image, const GHOST_XrDrawViewInfo &draw_info)
281{
282 XrSwapchainImageD3D11KHR &d3d_swapchain_image = reinterpret_cast<XrSwapchainImageD3D11KHR &>(
283 swapchain_image);
284
285 VkDeviceSize component_size = 4 * sizeof(uint8_t);
286 if (draw_info.swapchain_format == GHOST_kXrSwapchainFormatRGBA16F ||
287 draw_info.swapchain_format == GHOST_kXrSwapchainFormatRGBA16)
288 {
289 component_size = 4 * sizeof(uint16_t);
290 }
291
292 ID3D11Device *d3d_device = ghost_d3d_ctx_->device_;
293 ID3D11DeviceContext *d3d_device_ctx = ghost_d3d_ctx_->device_ctx_;
294 DXGI_FORMAT format;
295 ghost_format_to_dx_format(draw_info.swapchain_format, draw_info.expects_srgb_buffer, format);
296
297 /* Acquire frame buffer image. */
298 GHOST_VulkanOpenXRData openxr_data = {GHOST_kVulkanXRModeCPU};
299 ghost_ctx_.openxr_acquire_framebuffer_image_callback_(&openxr_data);
300
301 /* Upload the data to a D3D Texture */
302 D3D11_TEXTURE2D_DESC desc;
303 desc.Width = openxr_data.extent.width;
304 desc.Height = openxr_data.extent.height;
305 desc.MipLevels = 1;
306 desc.ArraySize = 1;
307 desc.Format = format;
308 desc.SampleDesc.Count = 1;
309 desc.SampleDesc.Quality = 0;
310 desc.Usage = D3D11_USAGE_IMMUTABLE;
311 desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
312 desc.CPUAccessFlags = 0;
313 desc.MiscFlags = 0;
314
315 D3D11_SUBRESOURCE_DATA data;
316 data.pSysMem = openxr_data.cpu.image_data;
317 data.SysMemPitch = component_size * openxr_data.extent.width;
318 data.SysMemSlicePitch = 0;
319
320 ID3D11Texture2D *texture = nullptr;
321 d3d_device->CreateTexture2D(&desc, &data, &texture);
322
323 /* Copy sub-resource of the uploaded texture to the swap-chain texture. */
324 d3d_device_ctx->CopySubresourceRegion(
325 d3d_swapchain_image.texture, 0, draw_info.ofsx, draw_info.ofsy, 0, texture, 0, nullptr);
326
327 /* Release the texture. */
328 texture->Release();
329
330 /* Release frame buffer image. */
331 ghost_ctx_.openxr_release_framebuffer_image_callback_(&openxr_data);
332}
333
335
336#endif
static std::optional< int64_t > choose_swapchain_format_from_candidates(const std::vector< int64_t > &gpu_binding_formats, const std::vector< int64_t > &runtime_formats)
static void ghost_format_to_dx_format(GHOST_TXrSwapchainFormat ghost_format, bool expects_srgb_buffer, DXGI_FORMAT &r_dx_format)
BMesh const char void * data
GHOST_IXrGraphicsBinding()=default
union GHOST_IXrGraphicsBinding::@316344103261324226161313226323037304250053225267 oxr_binding
static GHOST_ContextD3D * createOffscreenContextD3D()
static GHOST_TSuccess disposeContextD3D(GHOST_ContextD3D *context)
bool checkVersionRequirements(GHOST_Context &ghost_ctx, XrInstance instance, XrSystemId system_id, std::string *r_requirement_info) const override
std::list< std::vector< XrSwapchainImageD3D11KHR > > image_cache_
std::optional< int64_t > chooseSwapchainFormat(const std::vector< int64_t > &runtime_formats, GHOST_TXrSwapchainFormat &r_format, bool &r_is_srgb_format) const override
std::vector< XrSwapchainImageBaseHeader * > createSwapchainImages(uint32_t image_count) override
bool needsUpsideDownDrawing(GHOST_Context &ghost_ctx) const override
void initFromGhostContext(GHOST_Context &ghost_ctx, XrInstance instance, XrSystemId system_id) override
GHOST_XrGraphicsBindingOpenGLD3D(GHOST_Context &ghost_ctx)
void submitToSwapchainImage(XrSwapchainImageBaseHeader &swapchain_image, const GHOST_XrDrawViewInfo &draw_info) override
TEX_TEMPLATE DataVec texture(T, FltCoord, float=0.0f) RET
format