xref: /aosp_15_r20/external/mesa3d/src/gallium/winsys/d3d12/wgl/d3d12_wgl_framebuffer.cpp (revision 6104692788411f58d303aa86923a9ff6ecaded22)
1 /*
2  * Copyright © Microsoft Corporation
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the "Software"),
6  * to deal in the Software without restriction, including without limitation
7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8  * and/or sell copies of the Software, and to permit persons to whom the
9  * Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice (including the next
12  * paragraph) shall be included in all copies or substantial portions of the
13  * Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21  * IN THE SOFTWARE.
22  */
23 
24 #include "d3d12_wgl_public.h"
25 
26 #include <new>
27 
28 #include <windows.h>
29 #include <dxgi1_4.h>
30 #include <directx/d3d12.h>
31 #include <wrl.h>
32 #include <dxguids/dxguids.h>
33 
34 #include "util/u_memory.h"
35 #include "util/u_inlines.h"
36 #include "frontend/api.h"
37 #include "frontend/winsys_handle.h"
38 
39 #include "stw_device.h"
40 #include "stw_pixelformat.h"
41 #include "stw_winsys.h"
42 
43 #include "d3d12/d3d12_format.h"
44 #include "d3d12/d3d12_resource.h"
45 #include "d3d12/d3d12_screen.h"
46 
47 using Microsoft::WRL::ComPtr;
48 constexpr uint32_t num_buffers = 2;
49 
50 struct d3d12_wgl_framebuffer {
51    struct stw_winsys_framebuffer base;
52 
53    struct d3d12_screen *screen;
54    enum pipe_format pformat;
55    HWND window;
56    ComPtr<IDXGISwapChain3> swapchain;
57    HANDLE waitable_object;
58    int latency = 2;
59    struct pipe_resource *buffers[num_buffers];
60    bool single_buffered;
61    struct pipe_resource *offscreen_buffer;
62 };
63 
64 static struct d3d12_wgl_framebuffer *
d3d12_wgl_framebuffer(struct stw_winsys_framebuffer * fb)65 d3d12_wgl_framebuffer(struct stw_winsys_framebuffer *fb)
66 {
67    return (struct d3d12_wgl_framebuffer *)fb;
68 }
69 
70 static void
d3d12_wgl_framebuffer_destroy(struct stw_winsys_framebuffer * fb,pipe_context * ctx)71 d3d12_wgl_framebuffer_destroy(struct stw_winsys_framebuffer *fb,
72                               pipe_context *ctx)
73 {
74    struct d3d12_wgl_framebuffer *framebuffer = d3d12_wgl_framebuffer(fb);
75    struct pipe_fence_handle *fence = NULL;
76 
77    if (ctx) {
78       /* Ensure all resources are flushed */
79       ctx->flush(ctx, &fence, PIPE_FLUSH_HINT_FINISH);
80       if (fence) {
81          ctx->screen->fence_finish(ctx->screen, ctx, fence, OS_TIMEOUT_INFINITE);
82          ctx->screen->fence_reference(ctx->screen, &fence, NULL);
83       }
84    }
85 
86    for (int i = 0; i < num_buffers; ++i) {
87       if (framebuffer->buffers[i]) {
88          d3d12_resource_release(d3d12_resource(framebuffer->buffers[i]));
89          pipe_resource_reference(&framebuffer->buffers[i], NULL);
90       }
91    }
92 
93    if (framebuffer->offscreen_buffer) {
94       pipe_resource_reference(&framebuffer->offscreen_buffer, NULL);
95    }
96 
97    if (framebuffer->waitable_object)
98       CloseHandle(framebuffer->waitable_object);
99 
100    delete framebuffer;
101 }
102 
103 static void
d3d12_wgl_framebuffer_resize(stw_winsys_framebuffer * fb,pipe_context * ctx,pipe_resource * templ)104 d3d12_wgl_framebuffer_resize(stw_winsys_framebuffer *fb,
105                              pipe_context *ctx,
106                              pipe_resource *templ)
107 {
108    struct d3d12_wgl_framebuffer *framebuffer = d3d12_wgl_framebuffer(fb);
109    struct d3d12_dxgi_screen *screen = d3d12_dxgi_screen(framebuffer->screen);
110 
111    DXGI_SWAP_CHAIN_DESC1 desc = {};
112    desc.BufferCount = num_buffers;
113    desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT | DXGI_USAGE_SHADER_INPUT;
114    desc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING | DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT;
115    desc.Format = d3d12_get_format(templ->format);
116    desc.Width = templ->width0;
117    desc.Height = templ->height0;
118    desc.SampleDesc.Count = 1;
119    desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
120 
121    framebuffer->pformat = templ->format;
122 
123    if (!framebuffer->swapchain) {
124       ComPtr<IDXGISwapChain1> swapchain1;
125       if (FAILED(screen->factory->CreateSwapChainForHwnd(
126          screen->base.cmdqueue,
127          framebuffer->window,
128          &desc,
129          nullptr,
130          nullptr,
131          &swapchain1))) {
132          debug_printf("D3D12: failed to create swapchain");
133          return;
134       }
135 
136       swapchain1.As(&framebuffer->swapchain);
137 
138       screen->factory->MakeWindowAssociation(framebuffer->window,
139                                              DXGI_MWA_NO_WINDOW_CHANGES | DXGI_MWA_NO_ALT_ENTER | DXGI_MWA_NO_PRINT_SCREEN);
140 
141       framebuffer->waitable_object = framebuffer->swapchain->GetFrameLatencyWaitableObject();
142       WaitForSingleObject(framebuffer->waitable_object, INFINITE);
143 
144       framebuffer->swapchain->SetMaximumFrameLatency(framebuffer->latency);
145    }
146    else {
147       struct pipe_fence_handle *fence = NULL;
148 
149       /* Ensure all resources are flushed */
150       ctx->flush(ctx, &fence, PIPE_FLUSH_HINT_FINISH);
151       if (fence) {
152          ctx->screen->fence_finish(ctx->screen, ctx, fence, OS_TIMEOUT_INFINITE);
153          ctx->screen->fence_reference(ctx->screen, &fence, NULL);
154       }
155 
156       for (int i = 0; i < num_buffers; ++i) {
157          if (framebuffer->buffers[i]) {
158             d3d12_resource_release(d3d12_resource(framebuffer->buffers[i]));
159             pipe_resource_reference(&framebuffer->buffers[i], NULL);
160          }
161       }
162       if (FAILED(framebuffer->swapchain->ResizeBuffers(num_buffers, desc.Width, desc.Height, desc.Format, desc.Flags))) {
163          debug_printf("D3D12: failed to resize swapchain");
164       }
165    }
166 
167    for (uint32_t i = 0; i < num_buffers; ++i) {
168       ID3D12Resource *res;
169       framebuffer->swapchain->GetBuffer(i, IID_PPV_ARGS(&res));
170       if (!res)
171          continue;
172 
173       struct winsys_handle handle;
174       memset(&handle, 0, sizeof(handle));
175       handle.type = WINSYS_HANDLE_TYPE_D3D12_RES;
176       handle.format = framebuffer->pformat;
177       handle.com_obj = res;
178 
179       D3D12_RESOURCE_DESC res_desc = GetDesc(res);
180 
181       struct pipe_resource templ;
182       memset(&templ, 0, sizeof(templ));
183       templ.target = PIPE_TEXTURE_2D;
184       templ.format = framebuffer->pformat;
185       templ.width0 = res_desc.Width;
186       templ.height0 = res_desc.Height;
187       templ.depth0 = 1;
188       templ.array_size = res_desc.DepthOrArraySize;
189       templ.nr_samples = res_desc.SampleDesc.Count;
190       templ.last_level = res_desc.MipLevels - 1;
191       templ.bind = PIPE_BIND_DISPLAY_TARGET | PIPE_BIND_RENDER_TARGET;
192       templ.usage = PIPE_USAGE_DEFAULT;
193       templ.flags = 0;
194 
195       pipe_resource_reference(&framebuffer->buffers[i],
196                               screen->base.base.resource_from_handle(&screen->base.base, &templ, &handle,
197                                                                      PIPE_HANDLE_USAGE_FRAMEBUFFER_WRITE));
198    }
199 
200    if (framebuffer->single_buffered) {
201       if (framebuffer->offscreen_buffer) {
202          pipe_resource_reference(&framebuffer->offscreen_buffer, NULL);
203       }
204       struct pipe_resource local_templ = *templ;
205       local_templ.bind = PIPE_BIND_RENDER_TARGET | PIPE_BIND_SAMPLER_VIEW;
206       framebuffer->offscreen_buffer = screen->base.base.resource_create(&screen->base.base, &local_templ);
207    }
208 }
209 
210 static bool
d3d12_wgl_framebuffer_present(stw_winsys_framebuffer * fb,int interval)211 d3d12_wgl_framebuffer_present(stw_winsys_framebuffer *fb, int interval)
212 {
213    auto framebuffer = d3d12_wgl_framebuffer(fb);
214    if (!framebuffer->swapchain) {
215       debug_printf("D3D12: Cannot present; no swapchain");
216       return false;
217    }
218 
219    HRESULT hr;
220    if (interval < 1)
221       hr = framebuffer->swapchain->Present(0, DXGI_PRESENT_ALLOW_TEARING);
222    else
223       hr = framebuffer->swapchain->Present(interval, 0);
224 
225    if (hr == S_OK)
226       return WaitForSingleObject(framebuffer->waitable_object, 2000) == WAIT_OBJECT_0;
227    return false;
228 }
229 
230 static struct pipe_resource *
d3d12_wgl_framebuffer_get_resource(struct stw_winsys_framebuffer * pframebuffer,st_attachment_type statt)231 d3d12_wgl_framebuffer_get_resource(struct stw_winsys_framebuffer *pframebuffer,
232                                    st_attachment_type statt)
233 {
234    auto framebuffer = d3d12_wgl_framebuffer(pframebuffer);
235 
236    if (!framebuffer->swapchain)
237       return nullptr;
238 
239    if (framebuffer->single_buffered) {
240       assert(statt == ST_ATTACHMENT_FRONT_LEFT);
241       assert(framebuffer->offscreen_buffer);
242       pipe_reference(NULL, &framebuffer->offscreen_buffer->reference);
243       return framebuffer->offscreen_buffer;
244    }
245 
246    UINT index = framebuffer->swapchain->GetCurrentBackBufferIndex();
247    if (statt == ST_ATTACHMENT_FRONT_LEFT)
248       index = !index;
249 
250    assert(framebuffer->buffers[index]);
251    pipe_reference(NULL, &framebuffer->buffers[index]->reference);
252    return framebuffer->buffers[index];
253 }
254 
255 static void
d3d12_wgl_framebuffer_flush_frontbuffer(struct stw_winsys_framebuffer * pframebuffer,struct pipe_context * pipe)256 d3d12_wgl_framebuffer_flush_frontbuffer(struct stw_winsys_framebuffer *pframebuffer,
257                                         struct pipe_context *pipe)
258 {
259    auto framebuffer = d3d12_wgl_framebuffer(pframebuffer);
260    struct pipe_blit_info blit;
261 
262    memset(&blit, 0, sizeof(blit));
263    uint32_t index = framebuffer->swapchain->GetCurrentBackBufferIndex();
264    blit.dst.resource = framebuffer->buffers[index];
265    blit.dst.box.width = blit.dst.resource->width0;
266    blit.dst.box.height = blit.dst.resource->height0;
267    blit.dst.box.depth = 1;
268    blit.dst.format = blit.dst.resource->format;
269    blit.src.resource = framebuffer->offscreen_buffer;
270    blit.src.box.width = blit.src.resource->width0;
271    blit.src.box.height = blit.src.resource->height0;
272    blit.src.box.depth = 1;
273    blit.src.format = blit.src.resource->format;
274    blit.mask = PIPE_MASK_RGBA;
275    blit.filter = PIPE_TEX_FILTER_NEAREST;
276 
277    pipe->blit(pipe, &blit);
278    pipe->flush_resource(pipe, blit.dst.resource);
279    pipe->flush(pipe, NULL, 0);
280 }
281 
282 static void
d3d12_wgl_framebuffer_set_latency(struct stw_winsys_framebuffer * pframebuffer,int latency)283 d3d12_wgl_framebuffer_set_latency(struct stw_winsys_framebuffer *pframebuffer,
284                                   int latency)
285 {
286    if (latency < 1)
287       return;
288 
289    auto framebuffer = d3d12_wgl_framebuffer(pframebuffer);
290    int delta = latency - framebuffer->latency;
291    while (delta < 0 && framebuffer->waitable_object) {
292       WaitForSingleObject(framebuffer->waitable_object, INFINITE);
293       ++delta;
294    }
295    framebuffer->latency = latency;
296    if (framebuffer->swapchain)
297       framebuffer->swapchain->SetMaximumFrameLatency(latency);
298 }
299 
300 struct stw_winsys_framebuffer *
d3d12_wgl_create_framebuffer(struct pipe_screen * screen,HWND hWnd,int iPixelFormat)301 d3d12_wgl_create_framebuffer(struct pipe_screen *screen,
302                              HWND hWnd,
303                              int iPixelFormat)
304 {
305    const struct stw_pixelformat_info *pfi =
306       stw_pixelformat_get_info(iPixelFormat);
307    if ((pfi->pfd.dwFlags & PFD_SUPPORT_GDI))
308       return NULL;
309 
310    if (pfi->stvis.color_format != PIPE_FORMAT_B8G8R8A8_UNORM &&
311        pfi->stvis.color_format != PIPE_FORMAT_R8G8B8A8_UNORM &&
312        pfi->stvis.color_format != PIPE_FORMAT_R10G10B10A2_UNORM &&
313        pfi->stvis.color_format != PIPE_FORMAT_R16G16B16A16_FLOAT)
314       return NULL;
315 
316    struct d3d12_wgl_framebuffer *fb = CALLOC_STRUCT(d3d12_wgl_framebuffer);
317    if (!fb)
318       return NULL;
319 
320    new (fb) struct d3d12_wgl_framebuffer();
321 
322    fb->window = hWnd;
323    fb->screen = d3d12_screen(screen);
324    fb->single_buffered = (pfi->pfd.dwFlags & PFD_DOUBLEBUFFER) == 0;
325    fb->base.destroy = d3d12_wgl_framebuffer_destroy;
326    fb->base.resize = d3d12_wgl_framebuffer_resize;
327    fb->base.present = d3d12_wgl_framebuffer_present;
328    fb->base.get_resource = d3d12_wgl_framebuffer_get_resource;
329    fb->base.flush_frontbuffer = d3d12_wgl_framebuffer_flush_frontbuffer;
330    fb->base.set_latency = d3d12_wgl_framebuffer_set_latency;
331 
332    return &fb->base;
333 }
334