/* * Copyright 2005-2006 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Sun designates this * particular file as subject to the "Classpath" exception as provided * by Sun in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, * CA 95054 USA or visit www.sun.com if you need additional information or * have any questions. */ #include "dxInit.h" #include "ddrawUtils.h" #include "RegistryKey.h" #include "D3DTestRaster.h" #include "WindowsFlags.h" #include "D3DRuntimeTest.h" #include "D3DSurfaceData.h" #include "D3DUtils.h" #ifdef DEBUG void TestRasterOutput(byte *rasPtr, int x, int y, int w, int h, int scanStride, int pixelStride, TIntTestRaster goldenArray = NULL); #endif // DEBUG void PrintD3DCaps(int caps); /** * Test whether we should enable d3d rendering on this device. * This includes checking whether there were problems creating * the necessary offscreen surface, problems during any of the * rendering calls (Blts and d3d lines) and any rendering artifacts * caused by d3d lines. The rendering artifact tests are * performed by checking a pre-rendered test pattern (produced * by our software renderer) against that same pattern rendered * on this device. If there are any pixels which differ between * the two patterns we disable d3d line rendering on the device. * Differences in the test pattern rendering can be caused * by different rendering algorithms used by our software * renderer and the driver or hardware on this device. For example, * some Intel cards (e.g., i815) are known to use polygon renderers * for their lines, which sometimes result in wide lines. * The test pattern is stored in d3dTestRaster.h, which is generated * by a Java test program * (src/share/test/java2d/VolatileImage/D3DTestPattern/D3DTestPattern.java). */ int TestForBadHardware(DxCapabilities *dxCaps) { // Check this device against a list of bad d3d devices and // disable as necessary static WCHAR *badDeviceStrings[] = { L"Trident Video Accelerator", L"RAGE PRO", L"RAGE XL", L"Rage Fury", }; static int numBadDevices = 4; WCHAR *dxDeviceName = dxCaps->GetDeviceName(); for (int i = 0; i < numBadDevices; ++i) { if (wcsstr(dxDeviceName, badDeviceStrings[i]) != NULL) { // REMIND: For now, we disable d3d for all operations because // of one bad d3d device in the system. This is because we // should avoid registering the d3d rendering loops at the // Java level since we cannot use d3d at the native level. // A real fix would instead understand the difference between // a surface that could handle d3d native rendering and one // that could not and would use the appropriate rendering loop // so that disabling d3d on simply one device would be // sufficient. // Note that this disable-all approach is okay for now because // the single bad device (Trident) that triggers this error // is generally found on laptops, where multiple graphics // devices are not even possible, so disabling d3d for all // devices is equivalent to disabling d3d for this single // device. J2dRlsTraceLn1(J2D_TRACE_ERROR, "TestForBadHardware: Found match: %S. Test FAILED", badDeviceStrings[i]); return J2D_D3D_FAILURE; } } return J2D_D3D_HW_OK; } int TestTextureFormats(D3DContext *d3dContext) { int testRes = J2D_D3D_FAILURE; D3DTextureTable &table = d3dContext->GetTextureTable(); int pfExists; // Check that there's at least one valid pixel format // for each transparency type (opaque, bitmask, translucent) for (int t = TR_OPAQUE_IDX; t < TR_MAX_IDX; t++) { pfExists = FALSE; for (int d = DEPTH16_IDX; d < DEPTH_MAX_IDX; d++) { if (table[t][d].pfType != PF_INVALID) { pfExists = TRUE; break; } } if (pfExists == FALSE) { // couldn't find a pixel formap for this transparency type J2dRlsTraceLn1(J2D_TRACE_ERROR, "D3DTest::TestTextureFormats no texture formats"\ " for %d transparency", t); break; } } // we must have ARGB texture format (may be used for text rendering) if (pfExists == TRUE && table[TR_TRANSLUCENT_IDX][DEPTH32_IDX].pfType == PF_INT_ARGB) { testRes |= J2D_D3D_PIXEL_FORMATS_OK; } else { J2dRlsTraceLn1(J2D_TRACE_ERROR, "D3DTest::TestTextureFormats: FAILED pfType=%d", table[TR_TRANSLUCENT_IDX][DEPTH32_IDX].pfType); } return testRes; } int TestSetClip(JNIEnv *env, D3DContext *d3dContext, DDrawSurface *lpPlainSurface) { int testRes = J2D_D3D_FAILURE; if (SUCCEEDED(d3dContext->SetRenderTarget(lpPlainSurface))) { jobject clip = JNU_CallStaticMethodByName(env, NULL, "sun/java2d/pipe/Region", "getInstanceXYWH", "(IIII)Lsun/java2d/pipe/Region;", 0, 0, D3D_TEST_RASTER_W, D3D_TEST_RASTER_H).l; if (!JNU_IsNull(env, clip)) { if (SUCCEEDED(d3dContext->SetClip(env, clip, JNI_TRUE, 0, 0, D3D_TEST_RASTER_W, D3D_TEST_RASTER_H))) { testRes |= J2D_D3D_DEPTH_SURFACE_OK; } env->DeleteLocalRef(clip); } } return testRes; } int TestRenderingResults(DDrawSurface *lpPlainSurface, TIntTestRaster goldenArray) { // Now, check the results of the test raster against our d3d drawing SurfaceDataRasInfo rasInfo; if (FAILED(lpPlainSurface->Lock(NULL, &rasInfo, DDLOCK_WAIT, NULL))) { return J2D_D3D_FAILURE; } byte *rasPtr = (byte*)rasInfo.rasBase; int pixelStride = rasInfo.pixelStride; int scanStride = rasInfo.scanStride; for (int row = 0; row < D3D_TEST_RASTER_H; ++row) { byte *tmpRasPtr = rasPtr + row * scanStride; for (int col = 0; col < D3D_TEST_RASTER_W; ++col) { DWORD pixelVal; switch (pixelStride) { case 1: pixelVal = *tmpRasPtr; break; case 2: pixelVal = *((unsigned short*)tmpRasPtr); break; default: pixelVal = *((unsigned int*)tmpRasPtr); break; } tmpRasPtr += pixelStride; // The test is simple: if the test raster pixel has value 0, then // we expect 0 in the d3d surface. If the test raster has a nonzero // value, then we expect the d3d surface to also have non-zero value. // All other results represent failure. int goldenValue = (goldenArray[row][col] & 0x00ffffff); if ((goldenValue == 0 && pixelVal != 0) || (goldenValue != 0 && pixelVal == 0)) { J2dRlsTraceLn3(J2D_TRACE_WARNING, "TestRenderingResults: Quality test failed due "\ "to value %x at (%d, %d)", pixelVal, col, row); #ifdef DEBUG // This section is not necessary, but it might be // nice to know why we are failing D3DTest on some // systems. If tracing is enabled, this section will // produce an ascii representation of the test pattern, // the result on this device, and the pixels that were // in error. J2dTraceLn(J2D_TRACE_VERBOSE, "TestRaster:"); TestRasterOutput((byte*)goldenArray, 0, 0, D3D_TEST_RASTER_W, D3D_TEST_RASTER_H, D3D_TEST_RASTER_W*4, 4); J2dTraceLn(J2D_TRACE_VERBOSE, "D3D Raster:"); TestRasterOutput(rasPtr, 0, 0, D3D_TEST_RASTER_W, D3D_TEST_RASTER_H, scanStride, pixelStride); J2dTraceLn(J2D_TRACE_VERBOSE, "Deltas (x indicates problem pixel):"); TestRasterOutput(rasPtr, 0, 0, D3D_TEST_RASTER_W, D3D_TEST_RASTER_H, scanStride, pixelStride, goldenArray); #endif // DEBUG lpPlainSurface->Unlock(NULL); return J2D_D3D_FAILURE; } } } lpPlainSurface->Unlock(NULL); return (J2D_D3D_LINES_OK | J2D_D3D_LINE_CLIPPING_OK); } int TestLineRenderingQuality(JNIEnv *env, D3DContext *d3dContext, DDrawSurface *lpPlainSurface) { static J2D_XY_C_VERTEX lineVerts[] = { #ifdef USE_SINGLE_VERTEX_FORMAT { 0, 0, 0, 0xffffffff, 0.0f, 0.0f }, { 0, 0, 0, 0xffffffff, 0.0f, 0.0f }, { 0, 0, 0, 0xffffffff, 0.0f, 0.0f }, { 0, 0, 0, 0xffffffff, 0.0f, 0.0f }, { 0, 0, 0, 0xffffffff, 0.0f, 0.0f }, #else { 0, 0, 0, 0xffffffff }, // x, y, z, color { 0, 0, 0, 0xffffffff }, { 0, 0, 0, 0xffffffff }, { 0, 0, 0, 0xffffffff }, { 0, 0, 0, 0xffffffff }, #endif // USE_SINGLE_VERTEX_FORMAT }; IDirect3DDevice7 *d3dDevice = d3dContext->Get3DDevice(); HRESULT res; d3dDevice->Clear(0, NULL, D3DCLEAR_TARGET, 0x0, 0.0, 0); if (FAILED(d3dContext->BeginScene(STATE_RENDEROP))) { return J2D_D3D_FAILURE; } int i; for (i = 0; i < d3dNumTestLines * 4; i += 4) { lineVerts[0].x = d3dTestLines[i + 0]; lineVerts[0].y = d3dTestLines[i + 1]; lineVerts[1].x = d3dTestLines[i + 2]; lineVerts[1].y = d3dTestLines[i + 3]; if (FAILED(res = d3dDevice->DrawPrimitive(D3DPT_LINESTRIP, D3DFVF_J2D_XY_C, lineVerts, 2, 0))) { d3dContext->ForceEndScene(); return J2D_D3D_FAILURE; } // REMIND: needed for the test to pass on ATI some boards d3dDevice->DrawPrimitive(D3DPT_POINTLIST, D3DFVF_J2D_XY_C, &(lineVerts[1]), 1, 0); } for (i = 0; i < d3dNumTestRects * 4; i += 4) { float x1 = d3dTestRects[i + 0]; float y1 = d3dTestRects[i + 1]; float x2 = d3dTestRects[i + 2]; float y2 = d3dTestRects[i + 3]; D3DU_INIT_VERTEX_PENT_XY(lineVerts, x1, y1, x2, y2); if (FAILED(res = d3dDevice->DrawPrimitive(D3DPT_LINESTRIP, D3DFVF_J2D_XY_C, lineVerts, 5, 0))) { d3dContext->ForceEndScene(); return J2D_D3D_FAILURE; } } d3dContext->ForceEndScene(); // REMIND: add rendering of clipped lines return TestRenderingResults(lpPlainSurface, d3dTestRaster); } int TestTextureMappingQuality(JNIEnv *env, DDraw *ddObject, D3DContext *d3dContext, DDrawSurface *lpPlainSurface) { static J2DLVERTEX quadVerts[4] = { { 0.0f, 0.0f, 0.0f, 0xffffffff, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f, 0xffffffff, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f, 0xffffffff, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f, 0xffffffff, 0.0f, 0.0f } }; int testRes = TestTextureFormats(d3dContext); if (testRes & J2D_D3D_PIXEL_FORMATS_OK) { DDrawSurface *lpTexture = D3DUtils_CreateTexture(env, ddObject, d3dContext, TR_TRANSLUCENT, D3D_TEXTURE_RASTER_W, D3D_TEXTURE_RASTER_H); if (lpTexture) { D3DUtils_UploadIntImageToXRGBTexture(lpTexture, (int *)srcImageArray, D3D_TEXTURE_RASTER_W, D3D_TEXTURE_RASTER_H); float u2 = ((float)D3D_TEXTURE_RASTER_W) / (float)lpTexture->GetDXSurface()->GetWidth(); float v2 = ((float)D3D_TEXTURE_RASTER_H) / (float)lpTexture->GetDXSurface()->GetHeight(); D3DU_INIT_VERTEX_QUAD_UV(quadVerts, 0.0f, 0.0f, u2, v2); IDirect3DDevice7 *d3dDevice = d3dContext->Get3DDevice(); d3dDevice->Clear(0, NULL, D3DCLEAR_TARGET, 0x00000000, 0.0, 0); d3dContext->SetAlphaComposite(3/*SrcOver*/, 1.0f, D3DC_NO_CONTEXT_FLAGS); d3dDevice->SetTextureStageState(0, D3DTSS_MAGFILTER, D3DTFG_POINT); d3dDevice->SetTextureStageState(0, D3DTSS_MINFILTER, D3DTFG_POINT); HRESULT res; if (SUCCEEDED(res = d3dContext->BeginScene(STATE_BLITOP))) { DXSurface *dxSurface = lpTexture->GetDXSurface(); if (SUCCEEDED(d3dContext->SetTexture(dxSurface))) { for (int i = 0; i < d3dNumTextureRects * 4; i += 4) { float x1 = d3dTextureRects[i + 0]; float y1 = d3dTextureRects[i + 1]; float x2 = d3dTextureRects[i + 2]; float y2 = d3dTextureRects[i + 3]; D3DU_INIT_VERTEX_QUAD_XY(quadVerts, x1, y1, x2, y2); d3dDevice->DrawPrimitive(D3DPT_TRIANGLEFAN, D3DFVF_J2DLVERTEX, quadVerts, 4, 0); } } res = d3dContext->ForceEndScene(); d3dContext->SetTexture(NULL); } // REMIND: at this point we ignore the results of // the test. TestRenderingResults(lpPlainSurface, linInterpArray); if (SUCCEEDED(res)) { testRes |= (J2D_D3D_TR_TEXTURE_SURFACE_OK | J2D_D3D_TEXTURE_BLIT_OK | J2D_D3D_TEXTURE_TRANSFORM_OK); // REMIND: add tests for opaque and bitmask textures testRes |= (J2D_D3D_OP_TEXTURE_SURFACE_OK | J2D_D3D_BM_TEXTURE_SURFACE_OK); } delete lpTexture; } else { J2dRlsTraceLn(J2D_TRACE_ERROR, "TestTextureMappingQuality: "\ "CreateTexture(TRANSLUCENT) FAILED"); } } return testRes; } int TestD3DDevice(DDraw *ddObject, D3DContext *d3dContext, DxCapabilities *dxCaps) { int testRes = TestForBadHardware(dxCaps); if (!(testRes & J2D_D3D_HW_OK) || !d3dContext) { return testRes; } D3DDEVICEDESC7 d3dDevDesc; IDirect3DDevice7 *d3dDevice = d3dContext->Get3DDevice(); if (d3dDevice == NULL || FAILED(d3dDevice->GetCaps(&d3dDevDesc)) || FAILED(D3DUtils_CheckDeviceCaps(&d3dDevDesc))) { J2dRlsTraceLn(J2D_TRACE_ERROR, "TestD3DDevice: device caps testing FAILED"); return testRes; } testRes |= J2D_D3D_DEVICE_OK; JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2); DDrawSurface *lpPlainSurface = D3DUtils_CreatePlainSurface(env, ddObject, d3dContext, D3D_TEST_RASTER_W, D3D_TEST_RASTER_H); if (!lpPlainSurface) { J2dRlsTraceLn(J2D_TRACE_ERROR, "TestD3DDevice: CreatePlainSurface FAILED"); return testRes; } testRes |= J2D_D3D_PLAIN_SURFACE_OK; // Set identity transform if (FAILED(d3dContext->SetTransform(NULL, 0, 0, 0, 0, 0, 0))) { J2dRlsTraceLn(J2D_TRACE_ERROR, "TestD3DDevice: SetTransform FAILED"); delete lpPlainSurface; return testRes; } testRes |= J2D_D3D_SET_TRANSFORM_OK; // Test setting the target surface, create depth buffer, and // clip testRes |= TestSetClip(env, d3dContext, lpPlainSurface); if (!(testRes & J2D_D3D_DEPTH_SURFACE_OK)) { J2dRlsTraceLn(J2D_TRACE_ERROR, "TestD3DDevice: SetClip FAILED"); delete lpPlainSurface; return testRes; } // Test drawLines testRes |= TestLineRenderingQuality(env, d3dContext, lpPlainSurface); // Test texture mapping testRes |= TestTextureMappingQuality(env, ddObject, d3dContext, lpPlainSurface); d3dContext->SetRenderTarget(NULL); delete lpPlainSurface; return testRes; } #ifdef DEBUG /** * Output test raster (produced in D3DTest function). Utility * used in debugging only. Enable by setting J2D_TRACE_LEVEL=J2D_VERBOSE * prior to running application with debug java. The output from this will * be seen only if D3DTest fails. */ void TestRasterOutput(byte *rasPtr, int x, int y, int w, int h, int scanStride, int pixelStride, TIntTestRaster goldenArray) { int goldenValue; for (int traceRow = y; traceRow < h; ++traceRow) { byte *tmpRasPtr = rasPtr + traceRow * scanStride; for (int traceCol = x; traceCol < w; ++traceCol) { DWORD pixelVal; switch (pixelStride) { case 1: pixelVal = *tmpRasPtr; break; case 2: pixelVal = *((unsigned short*)tmpRasPtr); break; default: pixelVal = *((unsigned int*)tmpRasPtr) & 0x00ffffff; break; } tmpRasPtr += pixelStride; if (goldenArray == NULL) { if (pixelVal) { J2dTrace(J2D_TRACE_VERBOSE, "1"); } else { J2dTrace(J2D_TRACE_VERBOSE, "0"); } } else { goldenValue = (goldenArray[traceRow][traceCol] & 0x00ffffff); if ((goldenValue == 0 && pixelVal != 0) || (goldenValue != 0 && pixelVal == 0)) { J2dTrace(J2D_TRACE_VERBOSE, "x"); } else { J2dTrace(J2D_TRACE_VERBOSE, "-"); } } } J2dTrace(J2D_TRACE_VERBOSE, "\n"); } } #endif // DEBUG void PrintD3DCaps(int caps) { J2dTraceLn(J2D_TRACE_VERBOSE, "{") if (caps == J2D_D3D_FAILURE) { J2dTraceLn(J2D_TRACE_VERBOSE, " J2D_D3D_FAILURE"); } else { if (caps & J2D_D3D_DEPTH_SURFACE_OK) { J2dTraceLn(J2D_TRACE_VERBOSE, " J2D_D3D_DEPTH_SURFACE_OK,"); } if (caps & J2D_D3D_PLAIN_SURFACE_OK) { J2dTraceLn(J2D_TRACE_VERBOSE, " J2D_D3D_PLAIN_SURFACE_OK,"); } if (caps & J2D_D3D_OP_TEXTURE_SURFACE_OK) { J2dTraceLn(J2D_TRACE_VERBOSE, " J2D_D3D_OP_TEXTURE_SURFACE_OK,"); } if (caps & J2D_D3D_BM_TEXTURE_SURFACE_OK) { J2dTraceLn(J2D_TRACE_VERBOSE, " J2D_D3D_BM_TEXTURE_SURFACE_OK,"); } if (caps & J2D_D3D_TR_TEXTURE_SURFACE_OK) { J2dTraceLn(J2D_TRACE_VERBOSE, " J2D_D3D_TR_TEXTURE_SURFACE_OK,"); } if (caps & J2D_D3D_OP_RTT_SURFACE_OK) { J2dTraceLn(J2D_TRACE_VERBOSE, " J2D_D3D_OP_RTT_SURFACE_OK,"); } if (caps & J2D_D3D_LINE_CLIPPING_OK) { J2dTraceLn(J2D_TRACE_VERBOSE, " J2D_D3D_LINE_CLIPPING_OK,"); } if (caps & J2D_D3D_LINES_OK) { J2dTraceLn(J2D_TRACE_VERBOSE, " J2D_D3D_LINES_OK,"); } if (caps & J2D_D3D_TEXTURE_BLIT_OK) { J2dTraceLn(J2D_TRACE_VERBOSE, " J2D_D3D_TEXTURE_BLIT_OK,"); } if (caps & J2D_D3D_TEXTURE_TRANSFORM_OK) { J2dTraceLn(J2D_TRACE_VERBOSE, " J2D_D3D_TEXTURE_TRANSFORM_OK,"); } if (caps & J2D_D3D_DEVICE_OK) { J2dTraceLn(J2D_TRACE_VERBOSE, " J2D_D3D_DEVICE_OK,"); } if (caps & J2D_D3D_PIXEL_FORMATS_OK) { J2dTraceLn(J2D_TRACE_VERBOSE, " J2D_D3D_SET_TRANSFORM_OK,"); } if (caps & J2D_D3D_HW_OK) { J2dTraceLn(J2D_TRACE_VERBOSE, " J2D_D3D_HW_OK,"); } } J2dTraceLn(J2D_TRACE_VERBOSE, "}"); }