xref: /aosp_15_r20/external/mesa3d/src/gallium/winsys/d3d12/wgl/d3d12_wgl_framebuffer_xbox.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 <wrl.h>
30 
31 #include "util/u_memory.h"
32 #include "util/u_inlines.h"
33 #include "frontend/api.h"
34 #include "frontend/winsys_handle.h"
35 
36 #include "stw_device.h"
37 #include "stw_pixelformat.h"
38 #include "stw_winsys.h"
39 
40 #include "d3d12/d3d12_format.h"
41 #include "d3d12/d3d12_resource.h"
42 #include "d3d12/d3d12_screen.h"
43 
44 constexpr uint32_t num_buffers = 2;
45 static int current_backbuffer_index = 0;
46 static bool has_signaled_first_time = false;
47 static int cached_interval = 1;
48 
49 struct d3d12_wgl_framebuffer {
50    struct stw_winsys_framebuffer base;
51 
52    struct d3d12_screen *screen;
53    enum pipe_format pformat;
54    ID3D12Resource *images[num_buffers];
55    D3D12_CPU_DESCRIPTOR_HANDLE rtvs[num_buffers];
56    ID3D12DescriptorHeap *rtvHeap;
57    pipe_resource *buffers[num_buffers];
58 };
59 
60 static struct d3d12_wgl_framebuffer*
d3d12_wgl_framebuffer(struct stw_winsys_framebuffer * fb)61 d3d12_wgl_framebuffer(struct stw_winsys_framebuffer *fb)
62 {
63    return (struct d3d12_wgl_framebuffer *) fb;
64 }
65 
66 static void
d3d12_wgl_framebuffer_destroy(struct stw_winsys_framebuffer * fb,pipe_context * ctx)67 d3d12_wgl_framebuffer_destroy(struct stw_winsys_framebuffer *fb,
68                               pipe_context *ctx)
69 {
70    struct d3d12_wgl_framebuffer *framebuffer = d3d12_wgl_framebuffer(fb);
71    struct pipe_fence_handle *fence = NULL;
72 
73    if (ctx) {
74       /* Ensure all resources are flushed */
75       ctx->flush(ctx, &fence, PIPE_FLUSH_HINT_FINISH);
76       if (fence) {
77          ctx->screen->fence_finish(ctx->screen, ctx, fence, OS_TIMEOUT_INFINITE);
78          ctx->screen->fence_reference(ctx->screen, &fence, NULL);
79       }
80    }
81 
82    framebuffer->rtvHeap->Release();
83    for (int i = 0; i < num_buffers; ++i) {
84       if (framebuffer->buffers[i]) {
85          d3d12_resource_release(d3d12_resource(framebuffer->buffers[i]));
86          pipe_resource_reference(&framebuffer->buffers[i], NULL);
87       }
88    }
89 
90    delete framebuffer;
91 }
92 
93 static void
d3d12_wgl_framebuffer_resize(stw_winsys_framebuffer * fb,pipe_context * ctx,pipe_resource * templ)94 d3d12_wgl_framebuffer_resize(stw_winsys_framebuffer *fb,
95                              pipe_context *ctx,
96                              pipe_resource *templ)
97 {
98    struct d3d12_wgl_framebuffer *framebuffer = d3d12_wgl_framebuffer(fb);
99 
100    if (framebuffer->rtvHeap == NULL) {
101       D3D12_DESCRIPTOR_HEAP_DESC descHeapDesc = {};
102       descHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV;
103       descHeapDesc.NumDescriptors = num_buffers;
104       framebuffer->screen->dev->CreateDescriptorHeap(&descHeapDesc,
105                                                      IID_PPV_ARGS(&framebuffer->rtvHeap));
106    }
107 
108    // Release the old images
109    for (int i = 0; i < num_buffers; i++) {
110       if (framebuffer->buffers[i]) {
111          d3d12_resource_release(d3d12_resource(framebuffer->buffers[i]));
112          pipe_resource_reference(&framebuffer->buffers[i], NULL);
113       }
114    }
115 
116    D3D12_HEAP_PROPERTIES heapProps = {};
117    heapProps.Type = D3D12_HEAP_TYPE_DEFAULT;
118 
119    D3D12_RESOURCE_DESC resDesc;
120    resDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
121    resDesc.Width = templ->width0;
122    resDesc.Height = templ->height0;
123    resDesc.Alignment = 0;
124    resDesc.DepthOrArraySize = 1;
125    resDesc.Flags = D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET;
126    resDesc.Format = d3d12_get_format(templ->format);
127    resDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
128    resDesc.MipLevels = 1;
129    resDesc.SampleDesc.Count = 1;
130    resDesc.SampleDesc.Quality = 0;
131 
132    D3D12_CLEAR_VALUE optimizedClearValue = {};
133    optimizedClearValue.Format = resDesc.Format;
134 
135    D3D12_RENDER_TARGET_VIEW_DESC rtvDesc = {};
136    rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D;
137    rtvDesc.Format = resDesc.Format;
138    rtvDesc.Texture2D.MipSlice = 0;
139    rtvDesc.Texture2D.PlaneSlice = 0;
140 
141    for (int i = 0; i < num_buffers; i++) {
142       if (FAILED(framebuffer->screen->dev->CreateCommittedResource(
143          &heapProps,
144          D3D12_HEAP_FLAG_ALLOW_DISPLAY,
145          &resDesc,
146          D3D12_RESOURCE_STATE_PRESENT,
147          &optimizedClearValue,
148          IID_PPV_ARGS(&framebuffer->images[i])
149       ))) {
150          assert(0);
151       }
152 
153       framebuffer->rtvs[i].ptr =
154          framebuffer->rtvHeap->GetCPUDescriptorHandleForHeapStart().ptr +
155          ((int64_t)i * framebuffer->screen->dev->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV));
156 
157       framebuffer->screen->dev->CreateRenderTargetView(
158          framebuffer->images[i],
159          &rtvDesc,
160          framebuffer->rtvs[i]
161       );
162    }
163 
164    framebuffer->pformat = templ->format;
165 }
166 
167 static bool
d3d12_wgl_framebuffer_present(stw_winsys_framebuffer * fb,int interval)168 d3d12_wgl_framebuffer_present(stw_winsys_framebuffer *fb, int interval)
169 {
170    auto framebuffer = d3d12_wgl_framebuffer(fb);
171    D3D12XBOX_PRESENT_PLANE_PARAMETERS planeParams = {};
172    planeParams.Token = framebuffer->screen->frame_token;
173    planeParams.ResourceCount = 1;
174    planeParams.ppResources = &framebuffer->images[current_backbuffer_index];
175 
176    D3D12XBOX_PRESENT_PARAMETERS presentParams = {};
177    presentParams.Flags = (interval == 0) ?
178       D3D12XBOX_PRESENT_FLAG_IMMEDIATE :
179       D3D12XBOX_PRESENT_FLAG_NONE;
180 
181    int clamped_interval = CLAMP(interval, 1, 4); // SetFrameIntervalX only supports values [1,4]
182    if (cached_interval != clamped_interval) {
183       framebuffer->screen->dev->SetFrameIntervalX(
184          nullptr,
185          D3D12XBOX_FRAME_INTERVAL_60_HZ,
186          clamped_interval,
187          D3D12XBOX_FRAME_INTERVAL_FLAG_NONE
188       );
189       framebuffer->screen->dev->ScheduleFrameEventX(
190          D3D12XBOX_FRAME_EVENT_ORIGIN,
191          0,
192          nullptr,
193          D3D12XBOX_SCHEDULE_FRAME_EVENT_FLAG_NONE
194       );
195       cached_interval = clamped_interval;
196    }
197 
198    framebuffer->screen->cmdqueue->PresentX(1, &planeParams, &presentParams);
199 
200    current_backbuffer_index = !current_backbuffer_index;
201 
202    framebuffer->screen->frame_token = D3D12XBOX_FRAME_PIPELINE_TOKEN_NULL;
203    framebuffer->screen->dev->WaitFrameEventX(D3D12XBOX_FRAME_EVENT_ORIGIN, INFINITE,
204                                              nullptr, D3D12XBOX_WAIT_FRAME_EVENT_FLAG_NONE,
205                                              &framebuffer->screen->frame_token);
206 
207    return true;
208 }
209 
210 static struct pipe_resource*
d3d12_wgl_framebuffer_get_resource(struct stw_winsys_framebuffer * pframebuffer,st_attachment_type statt)211 d3d12_wgl_framebuffer_get_resource(struct stw_winsys_framebuffer *pframebuffer,
212                                    st_attachment_type statt)
213 {
214    auto framebuffer = d3d12_wgl_framebuffer(pframebuffer);
215    auto pscreen = &framebuffer->screen->base;
216 
217    UINT index = current_backbuffer_index;
218    if (statt == ST_ATTACHMENT_FRONT_LEFT)
219       index = !index;
220 
221    if (framebuffer->buffers[index]) {
222       pipe_reference(NULL, &framebuffer->buffers[index]->reference);
223       return framebuffer->buffers[index];
224    }
225 
226    ID3D12Resource *res = framebuffer->images[index];
227 
228    struct winsys_handle handle;
229    memset(&handle, 0, sizeof(handle));
230    handle.type = WINSYS_HANDLE_TYPE_D3D12_RES;
231    handle.format = framebuffer->pformat;
232    handle.com_obj = res;
233 
234    D3D12_RESOURCE_DESC res_desc = GetDesc(res);
235 
236    struct pipe_resource templ;
237    memset(&templ, 0, sizeof(templ));
238    templ.target = PIPE_TEXTURE_2D;
239    templ.format = framebuffer->pformat;
240    templ.width0 = res_desc.Width;
241    templ.height0 = res_desc.Height;
242    templ.depth0 = 1;
243    templ.array_size = res_desc.DepthOrArraySize;
244    templ.nr_samples = res_desc.SampleDesc.Count;
245    templ.last_level = res_desc.MipLevels - 1;
246    templ.bind = PIPE_BIND_DISPLAY_TARGET | PIPE_BIND_RENDER_TARGET;
247    templ.usage = PIPE_USAGE_DEFAULT;
248    templ.flags = 0;
249 
250    pipe_resource_reference(&framebuffer->buffers[index],
251       pscreen->resource_from_handle(pscreen, &templ, &handle,
252          PIPE_HANDLE_USAGE_FRAMEBUFFER_WRITE));
253    return framebuffer->buffers[index];
254 }
255 
256 struct stw_winsys_framebuffer*
d3d12_wgl_create_framebuffer(struct pipe_screen * screen,HWND hWnd,int iPixelFormat)257 d3d12_wgl_create_framebuffer(struct pipe_screen *screen,
258                              HWND hWnd,
259                              int iPixelFormat)
260 {
261    const struct stw_pixelformat_info *pfi =
262       stw_pixelformat_get_info(iPixelFormat);
263    if (!(pfi->pfd.dwFlags & PFD_DOUBLEBUFFER) ||
264       (pfi->pfd.dwFlags & PFD_SUPPORT_GDI))
265       return NULL;
266 
267    struct d3d12_wgl_framebuffer *fb = CALLOC_STRUCT(d3d12_wgl_framebuffer);
268    if (!fb)
269       return NULL;
270 
271    new (fb) struct d3d12_wgl_framebuffer();
272 
273    fb->screen = d3d12_screen(screen);
274    fb->images[0] = NULL;
275    fb->images[1] = NULL;
276    fb->rtvHeap = NULL;
277    fb->base.destroy = d3d12_wgl_framebuffer_destroy;
278    fb->base.resize = d3d12_wgl_framebuffer_resize;
279    fb->base.present = d3d12_wgl_framebuffer_present;
280    fb->base.get_resource = d3d12_wgl_framebuffer_get_resource;
281 
282    // Xbox applications must manually handle Suspend/Resume events on the Command Queue.
283    // To allow the application to access the queue, we store a pointer in the HWND's user data.
284    SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR) fb->screen->cmdqueue);
285 
286    // Schedule the frame interval and origin frame event
287    fb->screen->dev->SetFrameIntervalX(
288       nullptr,
289       D3D12XBOX_FRAME_INTERVAL_60_HZ,
290       cached_interval,
291       D3D12XBOX_FRAME_INTERVAL_FLAG_NONE
292    );
293    fb->screen->dev->ScheduleFrameEventX(
294       D3D12XBOX_FRAME_EVENT_ORIGIN,
295       0,
296       nullptr,
297       D3D12XBOX_SCHEDULE_FRAME_EVENT_FLAG_NONE
298    );
299 
300    return &fb->base;
301 }
302