st/nine: Refactor how user constbufs sizes are calculated
[mesa.git] / src / gallium / state_trackers / nine / texture9.c
1 /*
2 * Copyright 2011 Joakim Sindholt <opensource@zhasha.com>
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 * on the rights to use, copy, modify, merge, publish, distribute, sub
8 * license, and/or sell copies of the Software, and to permit persons to whom
9 * the 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 NON-INFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
19 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
20 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
21 * USE OR OTHER DEALINGS IN THE SOFTWARE. */
22
23 #include "device9.h"
24 #include "surface9.h"
25 #include "texture9.h"
26 #include "nine_helpers.h"
27 #include "nine_pipe.h"
28 #include "nine_dump.h"
29
30 #include "pipe/p_state.h"
31 #include "pipe/p_context.h"
32 #include "pipe/p_screen.h"
33 #include "util/u_inlines.h"
34 #include "util/u_resource.h"
35
36 #define DBG_CHANNEL DBG_TEXTURE
37
38 static HRESULT
39 NineTexture9_ctor( struct NineTexture9 *This,
40 struct NineUnknownParams *pParams,
41 UINT Width, UINT Height, UINT Levels,
42 DWORD Usage,
43 D3DFORMAT Format,
44 D3DPOOL Pool,
45 HANDLE *pSharedHandle )
46 {
47 struct pipe_screen *screen = pParams->device->screen;
48 struct pipe_resource *info = &This->base.base.info;
49 struct pipe_resource *resource;
50 enum pipe_format pf;
51 unsigned l;
52 D3DSURFACE_DESC sfdesc;
53 HRESULT hr;
54 void *user_buffer = NULL;
55
56 DBG("(%p) Width=%u Height=%u Levels=%u Usage=%s Format=%s Pool=%s "
57 "pSharedHandle=%p\n", This, Width, Height, Levels,
58 nine_D3DUSAGE_to_str(Usage),
59 d3dformat_to_string(Format), nine_D3DPOOL_to_str(Pool), pSharedHandle);
60
61 user_assert(!(Usage & D3DUSAGE_AUTOGENMIPMAP) ||
62 (Pool != D3DPOOL_SYSTEMMEM && Levels <= 1), D3DERR_INVALIDCALL);
63
64 /* TODO: implement buffer sharing (should work with cross process too)
65 *
66 * Gem names may have fit but they're depreciated and won't work on render-nodes.
67 * One solution is to use shm buffers. We would use a /dev/shm file, fill the first
68 * values to tell it is a nine buffer, the size, which function created it, etc,
69 * and then it would contain the data. The handle would be a number, corresponding to
70 * the file to read (/dev/shm/nine-share-4 for example would be 4).
71 *
72 * Wine just ignores the argument, which works only if the app creates the handle
73 * and won't use it. Instead of failing, we support that situation by putting an
74 * invalid handle, that we would fail to import. Please note that we don't advertise
75 * the flag indicating the support for that feature, but apps seem to not care.
76 */
77 user_assert(!pSharedHandle ||
78 Pool == D3DPOOL_SYSTEMMEM ||
79 Pool == D3DPOOL_DEFAULT, D3DERR_INVALIDCALL);
80
81 if (pSharedHandle && Pool == D3DPOOL_DEFAULT) {
82 if (!*pSharedHandle) {
83 DBG("Creating Texture with invalid handle. Importing will fail\n.");
84 *pSharedHandle = (HANDLE)1; /* Wine would keep it NULL */
85 pSharedHandle = NULL;
86 } else {
87 ERR("Application tries to use cross-process sharing feature. Nine "
88 "doesn't support it");
89 return D3DERR_INVALIDCALL;
90 }
91 }
92
93 if (Usage & D3DUSAGE_AUTOGENMIPMAP)
94 Levels = 0;
95
96 pf = d3d9_to_pipe_format(Format);
97 if (Format != D3DFMT_NULL && (pf == PIPE_FORMAT_NONE ||
98 !screen->is_format_supported(screen, pf, PIPE_TEXTURE_2D, 0, PIPE_BIND_SAMPLER_VIEW))) {
99 return D3DERR_INVALIDCALL;
100 }
101
102 info->screen = screen;
103 info->target = PIPE_TEXTURE_2D;
104 info->format = pf;
105 info->width0 = Width;
106 info->height0 = Height;
107 info->depth0 = 1;
108 if (Levels)
109 info->last_level = Levels - 1;
110 else
111 info->last_level = util_logbase2(MAX2(Width, Height));
112 info->array_size = 1;
113 info->nr_samples = 0;
114 info->bind = PIPE_BIND_SAMPLER_VIEW;
115 info->usage = PIPE_USAGE_DEFAULT;
116 info->flags = 0;
117
118 if (Usage & D3DUSAGE_RENDERTARGET)
119 info->bind |= PIPE_BIND_RENDER_TARGET;
120 if (Usage & D3DUSAGE_DEPTHSTENCIL)
121 info->bind |= PIPE_BIND_DEPTH_STENCIL;
122
123 if (Usage & D3DUSAGE_DYNAMIC) {
124 info->usage = PIPE_USAGE_DYNAMIC;
125 info->bind |=
126 PIPE_BIND_TRANSFER_READ |
127 PIPE_BIND_TRANSFER_WRITE;
128 }
129 if (pSharedHandle)
130 info->bind |= PIPE_BIND_SHARED;
131
132 if (Pool == D3DPOOL_SYSTEMMEM)
133 info->usage = PIPE_USAGE_STAGING;
134
135 if (pSharedHandle && *pSharedHandle) { /* Pool == D3DPOOL_SYSTEMMEM */
136 user_buffer = (void *)*pSharedHandle;
137 }
138
139 This->surfaces = CALLOC(info->last_level + 1, sizeof(*This->surfaces));
140 if (!This->surfaces)
141 return E_OUTOFMEMORY;
142
143 hr = NineBaseTexture9_ctor(&This->base, pParams, NULL, D3DRTYPE_TEXTURE, Format, Pool, Usage);
144 if (FAILED(hr))
145 return hr;
146 This->base.pstype = (Height == 1) ? 1 : 0;
147
148 /* Create all the surfaces right away.
149 * They manage backing storage, and transfers (LockRect) are deferred
150 * to them.
151 */
152 sfdesc.Format = Format;
153 sfdesc.Type = D3DRTYPE_SURFACE;
154 sfdesc.Usage = Usage;
155 sfdesc.Pool = Pool;
156 sfdesc.MultiSampleType = D3DMULTISAMPLE_NONE;
157 sfdesc.MultiSampleQuality = 0;
158
159 if (Pool == D3DPOOL_SYSTEMMEM)
160 resource = NULL;
161 else
162 resource = This->base.base.resource;
163
164 for (l = 0; l <= info->last_level; ++l) {
165 sfdesc.Width = u_minify(Width, l);
166 sfdesc.Height = u_minify(Height, l);
167
168 hr = NineSurface9_new(This->base.base.base.device, NineUnknown(This),
169 resource, user_buffer,
170 D3DRTYPE_TEXTURE, l, 0,
171 &sfdesc, &This->surfaces[l]);
172 if (FAILED(hr))
173 return hr;
174 }
175
176 This->dirty_rect.depth = 1; /* widht == 0 means empty, depth stays 1 */
177
178 if (pSharedHandle && !*pSharedHandle) {/* Pool == D3DPOOL_SYSTEMMEM */
179 *pSharedHandle = This->surfaces[0]->data;
180 }
181
182 return D3D_OK;
183 }
184
185 static void
186 NineTexture9_dtor( struct NineTexture9 *This )
187 {
188 unsigned l;
189
190 if (This->surfaces) {
191 /* The surfaces should have 0 references and be unbound now. */
192 for (l = 0; l <= This->base.base.info.last_level; ++l)
193 NineUnknown_Destroy(&This->surfaces[l]->base.base);
194 FREE(This->surfaces);
195 }
196
197 NineBaseTexture9_dtor(&This->base);
198 }
199
200 HRESULT WINAPI
201 NineTexture9_GetLevelDesc( struct NineTexture9 *This,
202 UINT Level,
203 D3DSURFACE_DESC *pDesc )
204 {
205 user_assert(Level <= This->base.base.info.last_level, D3DERR_INVALIDCALL);
206 user_assert(Level == 0 || !(This->base.base.usage & D3DUSAGE_AUTOGENMIPMAP),
207 D3DERR_INVALIDCALL);
208
209 *pDesc = This->surfaces[Level]->desc;
210
211 return D3D_OK;
212 }
213
214 HRESULT WINAPI
215 NineTexture9_GetSurfaceLevel( struct NineTexture9 *This,
216 UINT Level,
217 IDirect3DSurface9 **ppSurfaceLevel )
218 {
219 user_assert(Level <= This->base.base.info.last_level, D3DERR_INVALIDCALL);
220 user_assert(Level == 0 || !(This->base.base.usage & D3DUSAGE_AUTOGENMIPMAP),
221 D3DERR_INVALIDCALL);
222
223 NineUnknown_AddRef(NineUnknown(This->surfaces[Level]));
224 *ppSurfaceLevel = (IDirect3DSurface9 *)This->surfaces[Level];
225
226 return D3D_OK;
227 }
228
229 HRESULT WINAPI
230 NineTexture9_LockRect( struct NineTexture9 *This,
231 UINT Level,
232 D3DLOCKED_RECT *pLockedRect,
233 const RECT *pRect,
234 DWORD Flags )
235 {
236 DBG("This=%p Level=%u pLockedRect=%p pRect=%p Flags=%d\n",
237 This, Level, pLockedRect, pRect, Flags);
238
239 user_assert(Level <= This->base.base.info.last_level, D3DERR_INVALIDCALL);
240 user_assert(Level == 0 || !(This->base.base.usage & D3DUSAGE_AUTOGENMIPMAP),
241 D3DERR_INVALIDCALL);
242
243 return NineSurface9_LockRect(This->surfaces[Level], pLockedRect,
244 pRect, Flags);
245 }
246
247 HRESULT WINAPI
248 NineTexture9_UnlockRect( struct NineTexture9 *This,
249 UINT Level )
250 {
251 DBG("This=%p Level=%u\n", This, Level);
252
253 user_assert(Level <= This->base.base.info.last_level, D3DERR_INVALIDCALL);
254
255 return NineSurface9_UnlockRect(This->surfaces[Level]);
256 }
257
258 HRESULT WINAPI
259 NineTexture9_AddDirtyRect( struct NineTexture9 *This,
260 const RECT *pDirtyRect )
261 {
262 DBG("This=%p pDirtyRect=%p[(%u,%u)-(%u,%u)]\n", This, pDirtyRect,
263 pDirtyRect ? pDirtyRect->left : 0, pDirtyRect ? pDirtyRect->top : 0,
264 pDirtyRect ? pDirtyRect->right : 0, pDirtyRect ? pDirtyRect->bottom : 0);
265
266 /* Tracking dirty regions on DEFAULT or SYSTEMMEM resources is pointless,
267 * because we always write to the final storage. Just marked it dirty in
268 * case we need to generate mip maps.
269 */
270 if (This->base.base.pool != D3DPOOL_MANAGED) {
271 if (This->base.base.usage & D3DUSAGE_AUTOGENMIPMAP)
272 This->base.dirty_mip = TRUE;
273 return D3D_OK;
274 }
275 This->base.dirty = TRUE;
276
277 BASETEX_REGISTER_UPDATE(&This->base);
278
279 if (!pDirtyRect) {
280 u_box_origin_2d(This->base.base.info.width0,
281 This->base.base.info.height0, &This->dirty_rect);
282 } else {
283 struct pipe_box box;
284 rect_to_pipe_box_clamp(&box, pDirtyRect);
285 u_box_union_2d(&This->dirty_rect, &This->dirty_rect, &box);
286 }
287 return D3D_OK;
288 }
289
290 IDirect3DTexture9Vtbl NineTexture9_vtable = {
291 (void *)NineUnknown_QueryInterface,
292 (void *)NineUnknown_AddRef,
293 (void *)NineUnknown_Release,
294 (void *)NineUnknown_GetDevice, /* actually part of Resource9 iface */
295 (void *)NineResource9_SetPrivateData,
296 (void *)NineResource9_GetPrivateData,
297 (void *)NineResource9_FreePrivateData,
298 (void *)NineResource9_SetPriority,
299 (void *)NineResource9_GetPriority,
300 (void *)NineBaseTexture9_PreLoad,
301 (void *)NineResource9_GetType,
302 (void *)NineBaseTexture9_SetLOD,
303 (void *)NineBaseTexture9_GetLOD,
304 (void *)NineBaseTexture9_GetLevelCount,
305 (void *)NineBaseTexture9_SetAutoGenFilterType,
306 (void *)NineBaseTexture9_GetAutoGenFilterType,
307 (void *)NineBaseTexture9_GenerateMipSubLevels,
308 (void *)NineTexture9_GetLevelDesc,
309 (void *)NineTexture9_GetSurfaceLevel,
310 (void *)NineTexture9_LockRect,
311 (void *)NineTexture9_UnlockRect,
312 (void *)NineTexture9_AddDirtyRect
313 };
314
315 static const GUID *NineTexture9_IIDs[] = {
316 &IID_IDirect3DTexture9,
317 &IID_IDirect3DBaseTexture9,
318 &IID_IDirect3DResource9,
319 &IID_IUnknown,
320 NULL
321 };
322
323 HRESULT
324 NineTexture9_new( struct NineDevice9 *pDevice,
325 UINT Width, UINT Height, UINT Levels,
326 DWORD Usage,
327 D3DFORMAT Format,
328 D3DPOOL Pool,
329 struct NineTexture9 **ppOut,
330 HANDLE *pSharedHandle )
331 {
332 NINE_DEVICE_CHILD_NEW(Texture9, ppOut, pDevice,
333 Width, Height, Levels,
334 Usage, Format, Pool, pSharedHandle);
335 }