VirtualBox

source: vbox/trunk/src/VBox/Additions/WINNT/Graphics/Wine/wined3d/basetexture.c@ 21731

Last change on this file since 21731 was 21731, checked in by vboxsync, 16 years ago

crOpenGL: update to wine 1.1.26

  • Property svn:eol-style set to native
File size: 19.0 KB
Line 
1/*
2 * IWineD3DBaseTexture Implementation
3 *
4 * Copyright 2002-2004 Jason Edmeades
5 * Copyright 2002-2004 Raphael Junqueira
6 * Copyright 2005 Oliver Stieber
7 * Copyright 2007-2008 Stefan Dösinger for CodeWeavers
8 *
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
13 *
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
18 *
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 */
23
24/*
25 * Sun LGPL Disclaimer: For the avoidance of doubt, except that if any license choice
26 * other than GPL or LGPL is available it will apply instead, Sun elects to use only
27 * the Lesser General Public License version 2.1 (LGPLv2) at this time for any software where
28 * a choice of LGPL license versions is made available with the language indicating
29 * that LGPLv2 or any later version may be used, or where a choice of which version
30 * of the LGPL is applied is otherwise unspecified.
31 */
32
33#include "config.h"
34#include "wined3d_private.h"
35
36WINE_DEFAULT_DEBUG_CHANNEL(d3d_texture);
37#define GLINFO_LOCATION This->resource.wineD3DDevice->adapter->gl_info
38
39HRESULT basetexture_init(IWineD3DBaseTextureImpl *texture, UINT levels, WINED3DRESOURCETYPE resource_type,
40 IWineD3DDeviceImpl *device, UINT size, DWORD usage, const struct GlPixelFormatDesc *format_desc,
41 WINED3DPOOL pool, IUnknown *parent)
42{
43 HRESULT hr;
44
45 hr = resource_init((IWineD3DResource *)texture, resource_type, device, size, usage, format_desc, pool, parent);
46 if (FAILED(hr))
47 {
48 WARN("Failed to initialize resource, returning %#x\n", hr);
49 return hr;
50 }
51
52 texture->baseTexture.levels = levels;
53 texture->baseTexture.filterType = (usage & WINED3DUSAGE_AUTOGENMIPMAP) ? WINED3DTEXF_LINEAR : WINED3DTEXF_NONE;
54 texture->baseTexture.LOD = 0;
55 texture->baseTexture.dirty = TRUE;
56 texture->baseTexture.srgbDirty = TRUE;
57 texture->baseTexture.is_srgb = FALSE;
58 texture->baseTexture.pow2Matrix_identity = TRUE;
59
60 return WINED3D_OK;
61}
62
63void basetexture_cleanup(IWineD3DBaseTexture *iface)
64{
65 basetexture_unload(iface);
66 resource_cleanup((IWineD3DResource *)iface);
67}
68
69void basetexture_unload(IWineD3DBaseTexture *iface)
70{
71 IWineD3DTextureImpl *This = (IWineD3DTextureImpl *)iface;
72 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
73
74 if(This->baseTexture.textureName) {
75 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
76 ENTER_GL();
77 glDeleteTextures(1, &This->baseTexture.textureName);
78 This->baseTexture.textureName = 0;
79 LEAVE_GL();
80 }
81
82 if(This->baseTexture.srgbTextureName) {
83 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
84 ENTER_GL();
85 glDeleteTextures(1, &This->baseTexture.srgbTextureName);
86 This->baseTexture.srgbTextureName = 0;
87 LEAVE_GL();
88 }
89 This->baseTexture.dirty = TRUE;
90 This->baseTexture.srgbDirty = TRUE;
91}
92
93/* There is no OpenGL equivalent of setLOD, getLOD. All they do anyway is prioritize texture loading
94 * so just pretend that they work unless something really needs a failure. */
95DWORD basetexture_set_lod(IWineD3DBaseTexture *iface, DWORD LODNew)
96{
97 IWineD3DBaseTextureImpl *This = (IWineD3DBaseTextureImpl *)iface;
98
99 if (This->resource.pool != WINED3DPOOL_MANAGED) {
100 return WINED3DERR_INVALIDCALL;
101 }
102
103 if(LODNew >= This->baseTexture.levels)
104 LODNew = This->baseTexture.levels - 1;
105 This->baseTexture.LOD = LODNew;
106
107 TRACE("(%p) : set bogus LOD to %d\n", This, This->baseTexture.LOD);
108
109 return This->baseTexture.LOD;
110}
111
112DWORD basetexture_get_lod(IWineD3DBaseTexture *iface)
113{
114 IWineD3DBaseTextureImpl *This = (IWineD3DBaseTextureImpl *)iface;
115
116 if (This->resource.pool != WINED3DPOOL_MANAGED) {
117 return WINED3DERR_INVALIDCALL;
118 }
119
120 TRACE("(%p) : returning %d\n", This, This->baseTexture.LOD);
121
122 return This->baseTexture.LOD;
123}
124
125DWORD basetexture_get_level_count(IWineD3DBaseTexture *iface)
126{
127 IWineD3DBaseTextureImpl *This = (IWineD3DBaseTextureImpl *)iface;
128 TRACE("(%p) : returning %d\n", This, This->baseTexture.levels);
129 return This->baseTexture.levels;
130}
131
132HRESULT basetexture_set_autogen_filter_type(IWineD3DBaseTexture *iface, WINED3DTEXTUREFILTERTYPE FilterType)
133{
134 IWineD3DBaseTextureImpl *This = (IWineD3DBaseTextureImpl *)iface;
135 IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
136 UINT textureDimensions = IWineD3DBaseTexture_GetTextureDimensions(iface);
137
138 if (!(This->resource.usage & WINED3DUSAGE_AUTOGENMIPMAP)) {
139 TRACE("(%p) : returning invalid call\n", This);
140 return WINED3DERR_INVALIDCALL;
141 }
142 if(This->baseTexture.filterType != FilterType) {
143 /* What about multithreading? Do we want all the context overhead just to set this value?
144 * Or should we delay the applying until the texture is used for drawing? For now, apply
145 * immediately.
146 */
147 ActivateContext(device, device->lastActiveRenderTarget, CTXUSAGE_RESOURCELOAD);
148 ENTER_GL();
149 glBindTexture(textureDimensions, This->baseTexture.textureName);
150 checkGLcall("glBindTexture");
151 switch(FilterType) {
152 case WINED3DTEXF_NONE:
153 case WINED3DTEXF_POINT:
154 glTexParameteri(textureDimensions, GL_GENERATE_MIPMAP_HINT_SGIS, GL_FASTEST);
155 checkGLcall("glTexParameteri(textureDimensions, GL_GENERATE_MIPMAP_HINT_SGIS, GL_FASTEST)");
156
157 break;
158 case WINED3DTEXF_LINEAR:
159 glTexParameteri(textureDimensions, GL_GENERATE_MIPMAP_HINT_SGIS, GL_NICEST);
160 checkGLcall("glTexParameteri(textureDimensions, GL_GENERATE_MIPMAP_HINT_SGIS, GL_NICEST)");
161
162 break;
163 default:
164 WARN("Unexpected filter type %d, setting to GL_NICEST\n", FilterType);
165 glTexParameteri(textureDimensions, GL_GENERATE_MIPMAP_HINT_SGIS, GL_NICEST);
166 checkGLcall("glTexParameteri(textureDimensions, GL_GENERATE_MIPMAP_HINT_SGIS, GL_NICEST)");
167 }
168 LEAVE_GL();
169 }
170 This->baseTexture.filterType = FilterType;
171 TRACE("(%p) :\n", This);
172 return WINED3D_OK;
173}
174
175WINED3DTEXTUREFILTERTYPE basetexture_get_autogen_filter_type(IWineD3DBaseTexture *iface)
176{
177 IWineD3DBaseTextureImpl *This = (IWineD3DBaseTextureImpl *)iface;
178 FIXME("(%p) : stub\n", This);
179 if (!(This->resource.usage & WINED3DUSAGE_AUTOGENMIPMAP)) {
180 return WINED3DTEXF_NONE;
181 }
182 return This->baseTexture.filterType;
183}
184
185void basetexture_generate_mipmaps(IWineD3DBaseTexture *iface)
186{
187 IWineD3DBaseTextureImpl *This = (IWineD3DBaseTextureImpl *)iface;
188 /* TODO: implement filters using GL_SGI_generate_mipmaps http://oss.sgi.com/projects/ogl-sample/registry/SGIS/generate_mipmap.txt */
189 FIXME("(%p) : stub\n", This);
190 return ;
191}
192
193BOOL basetexture_set_dirty(IWineD3DBaseTexture *iface, BOOL dirty)
194{
195 BOOL old;
196 IWineD3DBaseTextureImpl *This = (IWineD3DBaseTextureImpl *)iface;
197 old = This->baseTexture.dirty || This->baseTexture.srgbDirty;
198 This->baseTexture.dirty = dirty;
199 This->baseTexture.srgbDirty = dirty;
200 return old;
201}
202
203BOOL basetexture_get_dirty(IWineD3DBaseTexture *iface)
204{
205 IWineD3DBaseTextureImpl *This = (IWineD3DBaseTextureImpl *)iface;
206 return This->baseTexture.dirty || This->baseTexture.srgbDirty;
207}
208
209/* Context activation is done by the caller. */
210HRESULT basetexture_bind(IWineD3DBaseTexture *iface, BOOL srgb, BOOL *set_surface_desc)
211{
212 IWineD3DBaseTextureImpl *This = (IWineD3DBaseTextureImpl *)iface;
213 HRESULT hr = WINED3D_OK;
214 UINT textureDimensions;
215 BOOL isNewTexture = FALSE;
216 GLuint *texture;
217 DWORD *states;
218 TRACE("(%p) : About to bind texture\n", This);
219
220 This->baseTexture.is_srgb = srgb; /* SRGB mode cache for PreLoad calls outside drawprim */
221 if(srgb) {
222 texture = &This->baseTexture.srgbTextureName;
223 states = This->baseTexture.srgbstates;
224 } else {
225 texture = &This->baseTexture.textureName;
226 states = This->baseTexture.states;
227 }
228
229 textureDimensions = IWineD3DBaseTexture_GetTextureDimensions(iface);
230 ENTER_GL();
231 /* Generate a texture name if we don't already have one */
232 if (*texture == 0) {
233 *set_surface_desc = TRUE;
234 glGenTextures(1, texture);
235 checkGLcall("glGenTextures");
236 TRACE("Generated texture %d\n", *texture);
237 if (This->resource.pool == WINED3DPOOL_DEFAULT) {
238 /* Tell opengl to try and keep this texture in video ram (well mostly) */
239 GLclampf tmp;
240 tmp = 0.9f;
241 glPrioritizeTextures(1, texture, &tmp);
242
243 }
244 /* Initialise the state of the texture object
245 to the openGL defaults, not the directx defaults */
246 states[WINED3DTEXSTA_ADDRESSU] = WINED3DTADDRESS_WRAP;
247 states[WINED3DTEXSTA_ADDRESSV] = WINED3DTADDRESS_WRAP;
248 states[WINED3DTEXSTA_ADDRESSW] = WINED3DTADDRESS_WRAP;
249 states[WINED3DTEXSTA_BORDERCOLOR] = 0;
250 states[WINED3DTEXSTA_MAGFILTER] = WINED3DTEXF_LINEAR;
251 states[WINED3DTEXSTA_MINFILTER] = WINED3DTEXF_POINT; /* GL_NEAREST_MIPMAP_LINEAR */
252 states[WINED3DTEXSTA_MIPFILTER] = WINED3DTEXF_LINEAR; /* GL_NEAREST_MIPMAP_LINEAR */
253 states[WINED3DTEXSTA_MAXMIPLEVEL] = 0;
254 states[WINED3DTEXSTA_MAXANISOTROPY] = 0;
255 states[WINED3DTEXSTA_SRGBTEXTURE] = 0;
256 states[WINED3DTEXSTA_ELEMENTINDEX] = 0;
257 states[WINED3DTEXSTA_DMAPOFFSET] = 0;
258 states[WINED3DTEXSTA_TSSADDRESSW] = WINED3DTADDRESS_WRAP;
259 IWineD3DBaseTexture_SetDirty(iface, TRUE);
260 isNewTexture = TRUE;
261
262 if(This->resource.usage & WINED3DUSAGE_AUTOGENMIPMAP) {
263 /* This means double binding the texture at creation, but keeps the code simpler all
264 * in all, and the run-time path free from additional checks
265 */
266 glBindTexture(textureDimensions, *texture);
267 checkGLcall("glBindTexture");
268 glTexParameteri(textureDimensions, GL_GENERATE_MIPMAP_SGIS, GL_TRUE);
269 checkGLcall("glTexParameteri(textureDimensions, GL_GENERATE_MIPMAP_SGIS, GL_TRUE)");
270 }
271 } else {
272 *set_surface_desc = FALSE;
273 }
274
275 /* Bind the texture */
276 if (*texture != 0) {
277 glBindTexture(textureDimensions, *texture);
278 checkGLcall("glBindTexture");
279 if (isNewTexture) {
280 /* For a new texture we have to set the textures levels after binding the texture.
281 * In theory this is all we should ever have to do, but because ATI's drivers are broken, we
282 * also need to set the texture dimensions before the texture is set
283 * Beware that texture rectangles do not support mipmapping, but set the maxmiplevel if we're
284 * relying on the partial GL_ARB_texture_non_power_of_two emulation with texture rectangles
285 * (ie, do not care for cond_np2 here, just look for GL_TEXTURE_RECTANGLE_ARB)
286 */
287 if(textureDimensions != GL_TEXTURE_RECTANGLE_ARB) {
288 TRACE("Setting GL_TEXTURE_MAX_LEVEL to %d\n", This->baseTexture.levels - 1);
289 glTexParameteri(textureDimensions, GL_TEXTURE_MAX_LEVEL, This->baseTexture.levels - 1);
290 checkGLcall("glTexParameteri(textureDimensions, GL_TEXTURE_MAX_LEVEL, This->baseTexture.levels)");
291 }
292 if(textureDimensions==GL_TEXTURE_CUBE_MAP_ARB) {
293 /* Cubemaps are always set to clamp, regardless of the sampler state. */
294 glTexParameteri(textureDimensions, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
295 glTexParameteri(textureDimensions, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
296 glTexParameteri(textureDimensions, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
297 }
298 }
299 } else { /* this only happened if we've run out of openGL textures */
300 WARN("This texture doesn't have an openGL texture assigned to it\n");
301 hr = WINED3DERR_INVALIDCALL;
302 }
303
304 LEAVE_GL();
305 return hr;
306}
307
308/* GL locking is done by the caller */
309static inline void apply_wrap(const GLint textureDimensions, const DWORD state, const GLint type,
310 BOOL cond_np2) {
311 GLint wrapParm;
312
313 if (state < minLookup[WINELOOKUP_WARPPARAM] || state > maxLookup[WINELOOKUP_WARPPARAM]) {
314 FIXME("Unrecognized or unsupported WINED3DTADDRESS_U value %d\n", state);
315 } else {
316 if(textureDimensions==GL_TEXTURE_CUBE_MAP_ARB) {
317 /* Cubemaps are always set to clamp, regardless of the sampler state. */
318 wrapParm = GL_CLAMP_TO_EDGE;
319 } else if(cond_np2) {
320 if(state == WINED3DTADDRESS_WRAP) {
321 wrapParm = GL_CLAMP_TO_EDGE;
322 } else {
323 wrapParm = stateLookup[WINELOOKUP_WARPPARAM][state - minLookup[WINELOOKUP_WARPPARAM]];
324 }
325 } else {
326 wrapParm = stateLookup[WINELOOKUP_WARPPARAM][state - minLookup[WINELOOKUP_WARPPARAM]];
327 }
328 TRACE("Setting WRAP_S to %d for %x\n", wrapParm, textureDimensions);
329 glTexParameteri(textureDimensions, type, wrapParm);
330 checkGLcall("glTexParameteri(..., type, wrapParm)");
331 }
332}
333
334/* GL locking is done by the caller (state handler) */
335void basetexture_apply_state_changes(IWineD3DBaseTexture *iface,
336 const DWORD textureStates[WINED3D_HIGHEST_TEXTURE_STATE + 1],
337 const DWORD samplerStates[WINED3D_HIGHEST_SAMPLER_STATE + 1])
338{
339 IWineD3DBaseTextureImpl *This = (IWineD3DBaseTextureImpl *)iface;
340 DWORD state, *states;
341 GLint textureDimensions = IWineD3DBaseTexture_GetTextureDimensions(iface);
342 BOOL cond_np2 = IWineD3DBaseTexture_IsCondNP2(iface);
343
344 TRACE("iface %p, textureStates %p, samplerStates %p\n", iface, textureStates, samplerStates);
345
346 if(This->baseTexture.is_srgb) {
347 states = This->baseTexture.srgbstates;
348 } else {
349 states = This->baseTexture.states;
350 }
351
352 /* This function relies on the correct texture being bound and loaded. */
353
354 if(samplerStates[WINED3DSAMP_ADDRESSU] != states[WINED3DTEXSTA_ADDRESSU]) {
355 state = samplerStates[WINED3DSAMP_ADDRESSU];
356 apply_wrap(textureDimensions, state, GL_TEXTURE_WRAP_S, cond_np2);
357 states[WINED3DTEXSTA_ADDRESSU] = state;
358 }
359
360 if(samplerStates[WINED3DSAMP_ADDRESSV] != states[WINED3DTEXSTA_ADDRESSV]) {
361 state = samplerStates[WINED3DSAMP_ADDRESSV];
362 apply_wrap(textureDimensions, state, GL_TEXTURE_WRAP_T, cond_np2);
363 states[WINED3DTEXSTA_ADDRESSV] = state;
364 }
365
366 if(samplerStates[WINED3DSAMP_ADDRESSW] != states[WINED3DTEXSTA_ADDRESSW]) {
367 state = samplerStates[WINED3DSAMP_ADDRESSW];
368 apply_wrap(textureDimensions, state, GL_TEXTURE_WRAP_R, cond_np2);
369 states[WINED3DTEXSTA_ADDRESSW] = state;
370 }
371
372 if(samplerStates[WINED3DSAMP_BORDERCOLOR] != states[WINED3DTEXSTA_BORDERCOLOR]) {
373 float col[4];
374
375 state = samplerStates[WINED3DSAMP_BORDERCOLOR];
376 D3DCOLORTOGLFLOAT4(state, col);
377 TRACE("Setting border color for %u to %x\n", textureDimensions, state);
378 glTexParameterfv(textureDimensions, GL_TEXTURE_BORDER_COLOR, &col[0]);
379 checkGLcall("glTexParameteri(..., GL_TEXTURE_BORDER_COLOR, ...)");
380 states[WINED3DTEXSTA_BORDERCOLOR] = state;
381 }
382
383 if(samplerStates[WINED3DSAMP_MAGFILTER] != states[WINED3DTEXSTA_MAGFILTER]) {
384 GLint glValue;
385 state = samplerStates[WINED3DSAMP_MAGFILTER];
386 if (state > WINED3DTEXF_ANISOTROPIC) {
387 FIXME("Unrecognized or unsupported MAGFILTER* value %d\n", state);
388 } else {
389 glValue = This->baseTexture.magLookup[state - WINED3DTEXF_NONE];
390 TRACE("ValueMAG=%d setting MAGFILTER to %x\n", state, glValue);
391 glTexParameteri(textureDimensions, GL_TEXTURE_MAG_FILTER, glValue);
392 /* We need to reset the Anisotropic filtering state when we change the mag filter to WINED3DTEXF_ANISOTROPIC (this seems a bit weird, check the documentation to see how it should be switched off. */
393 if (GL_SUPPORT(EXT_TEXTURE_FILTER_ANISOTROPIC) && WINED3DTEXF_ANISOTROPIC == state &&
394 !cond_np2) {
395 glTexParameteri(textureDimensions, GL_TEXTURE_MAX_ANISOTROPY_EXT, samplerStates[WINED3DSAMP_MAXANISOTROPY]);
396 }
397 states[WINED3DTEXSTA_MAGFILTER] = state;
398 }
399 }
400
401 if((samplerStates[WINED3DSAMP_MINFILTER] != states[WINED3DTEXSTA_MINFILTER] ||
402 samplerStates[WINED3DSAMP_MIPFILTER] != states[WINED3DTEXSTA_MIPFILTER] ||
403 samplerStates[WINED3DSAMP_MAXMIPLEVEL] != states[WINED3DTEXSTA_MAXMIPLEVEL])) {
404 GLint glValue;
405
406 states[WINED3DTEXSTA_MIPFILTER] = samplerStates[WINED3DSAMP_MIPFILTER];
407 states[WINED3DTEXSTA_MINFILTER] = samplerStates[WINED3DSAMP_MINFILTER];
408 states[WINED3DTEXSTA_MAXMIPLEVEL] = samplerStates[WINED3DSAMP_MAXMIPLEVEL];
409
410 if (states[WINED3DTEXSTA_MINFILTER] > WINED3DTEXF_ANISOTROPIC ||
411 states[WINED3DTEXSTA_MIPFILTER] > WINED3DTEXF_LINEAR)
412 {
413
414 FIXME("Unrecognized or unsupported D3DSAMP_MINFILTER value %d D3DSAMP_MIPFILTER value %d\n",
415 states[WINED3DTEXSTA_MINFILTER],
416 states[WINED3DTEXSTA_MIPFILTER]);
417 }
418 glValue = This->baseTexture.minMipLookup
419 [min(max(samplerStates[WINED3DSAMP_MINFILTER],WINED3DTEXF_NONE), WINED3DTEXF_ANISOTROPIC)]
420 .mip[min(max(samplerStates[WINED3DSAMP_MIPFILTER],WINED3DTEXF_NONE), WINED3DTEXF_LINEAR)];
421
422 TRACE("ValueMIN=%d, ValueMIP=%d, setting MINFILTER to %x\n",
423 samplerStates[WINED3DSAMP_MINFILTER],
424 samplerStates[WINED3DSAMP_MIPFILTER], glValue);
425 glTexParameteri(textureDimensions, GL_TEXTURE_MIN_FILTER, glValue);
426 checkGLcall("glTexParameter GL_TEXTURE_MIN_FILTER, ...");
427
428 if(!cond_np2) {
429 if(states[WINED3DTEXSTA_MIPFILTER] == WINED3DTEXF_NONE) {
430 glValue = 0;
431 } else if(states[WINED3DTEXSTA_MAXMIPLEVEL] >= This->baseTexture.levels) {
432 glValue = This->baseTexture.levels - 1;
433 } else {
434 glValue = states[WINED3DTEXSTA_MAXMIPLEVEL];
435 }
436 glTexParameteri(textureDimensions, GL_TEXTURE_BASE_LEVEL, glValue);
437 }
438 }
439
440 if(samplerStates[WINED3DSAMP_MAXANISOTROPY] != states[WINED3DTEXSTA_MAXANISOTROPY]) {
441 if (GL_SUPPORT(EXT_TEXTURE_FILTER_ANISOTROPIC) && !cond_np2) {
442 glTexParameteri(textureDimensions, GL_TEXTURE_MAX_ANISOTROPY_EXT, samplerStates[WINED3DSAMP_MAXANISOTROPY]);
443 checkGLcall("glTexParameteri GL_TEXTURE_MAX_ANISOTROPY_EXT ...");
444 } else {
445 WARN("Unsupported in local OpenGL implementation: glTexParameteri GL_TEXTURE_MAX_ANISOTROPY_EXT\n");
446 }
447 states[WINED3DTEXSTA_MAXANISOTROPY] = samplerStates[WINED3DSAMP_MAXANISOTROPY];
448 }
449}
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette