/* * Copyright (c) 2007, 2016, Oracle and/or its affiliates. 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. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ #include "D3DPipeline.h" #include "jlong.h" #include "GraphicsPrimitiveMgr.h" #include "D3DContext.h" #include "D3DSurfaceData.h" #include "D3DBufImgOps.h" #include "D3DPaints.h" #include "D3DRenderQueue.h" #include "D3DShaders.h" #include "D3DTextRenderer.h" #include "D3DPipelineManager.h" #include "D3DGlyphCache.h" typedef struct { D3DBLEND src; D3DBLEND dst; } D3DBlendRule; /** * This table contains the standard blending rules (or Porter-Duff compositing * factors) used in SetRenderState(), indexed by the rule constants from the * AlphaComposite class. */ D3DBlendRule StdBlendRules[] = { { D3DBLEND_ZERO, D3DBLEND_ZERO }, /* 0 - Nothing */ { D3DBLEND_ZERO, D3DBLEND_ZERO }, /* 1 - RULE_Clear */ { D3DBLEND_ONE, D3DBLEND_ZERO }, /* 2 - RULE_Src */ { D3DBLEND_ONE, D3DBLEND_INVSRCALPHA }, /* 3 - RULE_SrcOver */ { D3DBLEND_INVDESTALPHA, D3DBLEND_ONE }, /* 4 - RULE_DstOver */ { D3DBLEND_DESTALPHA, D3DBLEND_ZERO }, /* 5 - RULE_SrcIn */ { D3DBLEND_ZERO, D3DBLEND_SRCALPHA }, /* 6 - RULE_DstIn */ { D3DBLEND_INVDESTALPHA, D3DBLEND_ZERO }, /* 7 - RULE_SrcOut */ { D3DBLEND_ZERO, D3DBLEND_INVSRCALPHA }, /* 8 - RULE_DstOut */ { D3DBLEND_ZERO, D3DBLEND_ONE }, /* 9 - RULE_Dst */ { D3DBLEND_DESTALPHA, D3DBLEND_INVSRCALPHA }, /*10 - RULE_SrcAtop */ { D3DBLEND_INVDESTALPHA, D3DBLEND_SRCALPHA }, /*11 - RULE_DstAtop */ { D3DBLEND_INVDESTALPHA, D3DBLEND_INVSRCALPHA }, /*12 - RULE_AlphaXor*/ }; void D3DUtils_SetOrthoMatrixOffCenterLH(D3DMATRIX *m, float width, float height) { ZeroMemory(m, sizeof(D3DMATRIX)); m->_11 = 2.0f/width; m->_22 = -2.0f/height; m->_33 = 0.5f; m->_44 = 1.0f; m->_41 = -1.0f; m->_42 = 1.0f; m->_43 = 0.5f; } void D3DUtils_SetIdentityMatrix(D3DMATRIX *m) { m->_12 = m->_13 = m->_14 = m->_21 = m->_23 = m->_24 = 0.0f; m->_31 = m->_32 = m->_34 = m->_41 = m->_42 = m->_43 = 0.0f; m->_11 = m->_22 = m->_33 = m->_44 = 1.0f; } // the following methods are copies of the AffineTransform's class // corresponding methods, with these changes to the indexes: // 00 -> 11 // 11 -> 22 // 01 -> 21 // 10 -> 12 // 02 -> 41 // 12 -> 42 void D3DUtils_2DConcatenateM(D3DMATRIX *m, D3DMATRIX *m1) { float M0, M1; float T00, T10, T01, T11; float T02, T12; T00 = m1->_11; T01 = m1->_21; T02 = m1->_41; T10 = m1->_12; T11 = m1->_22; T12 = m1->_42; M0 = m->_11; M1 = m->_21; m->_11 = T00 * M0 + T10 * M1; m->_21 = T01 * M0 + T11 * M1; m->_41 += T02 * M0 + T12 * M1; M0 = m->_12; M1 = m->_22; m->_12 = T00 * M0 + T10 * M1; m->_22 = T01 * M0 + T11 * M1; m->_42 += T02 * M0 + T12 * M1; } #ifdef UPDATE_TX void D3DUtils_2DScaleM(D3DMATRIX *m, float sx, float sy) { m->_11 *= sx; m->_22 *= sy; } void D3DUtils_2DInvertM(D3DMATRIX *m) { float M11, M21, M41; float M12, M22, M42; float det; M11 = m->_11; M21 = m->_21; M41 = m->_41; M12 = m->_12; M22 = m->_22; M42 = m->_42; det = M11 * M22 - M21 * M12; if (fabs(det) <= 0.0000000001f) { memset(m, 0, sizeof(D3DMATRIX)); return; } m->_11 = M22 / det; m->_12 = -M12 / det; m->_21 = -M21 / det; m->_22 = M11 / det; m->_41 = (M21 * M42 - M22 * M41) / det; m->_42 = (M12 * M41 - M11 * M42) / det; } void D3DUtils_2DTranslateM(D3DMATRIX *m, float tx, float ty) { m->_41 = tx * m->_11 + ty * m->_21 + m->_41; m->_42 = tx * m->_12 + ty * m->_22 + m->_42; } void D3DUtils_2DTransformXY(D3DMATRIX *m, float *px, float *py) { float x = *px; float y = *py; *px = x * m->_11 + y * m->_21 + m->_41; *py = x * m->_12 + y * m->_22 + m->_42; } void D3DUtils_2DInverseTransformXY(D3DMATRIX *m, float *px, float *py) { float x = *px, y = *py; x -= m->_41; y -= m->_42; float det = m->_11 * m->_22 - m->_21 * m->_12; if (fabs(det) < 0.0000000001f) { *px = 0.0f; *py = 0.0f; } else { *px = (x * m->_22 - y * m->_21) / det; *py = (y * m->_11 - x * m->_12) / det; } } #endif // UPDATE_TX static void D3DContext_DisposeShader(jlong programID) { IDirect3DPixelShader9 *shader = (IDirect3DPixelShader9 *)jlong_to_ptr(programID); J2dTraceLn(J2D_TRACE_INFO, "D3DContext_DisposeShader"); SAFE_RELEASE(shader); } // static HRESULT D3DContext::CreateInstance(IDirect3D9 *pd3d9, UINT adapter, D3DContext **ppCtx) { HRESULT res; *ppCtx = new D3DContext(pd3d9, adapter); if (FAILED(res = (*ppCtx)->InitContext())) { delete *ppCtx; *ppCtx = NULL; } return res; } D3DContext::D3DContext(IDirect3D9 *pd3d, UINT adapter) { J2dTraceLn(J2D_TRACE_INFO, "D3DContext::D3DContext"); J2dTraceLn1(J2D_TRACE_VERBOSE, " pd3d=0x%x", pd3d); pd3dObject = pd3d; pd3dDevice = NULL; adapterOrdinal = adapter; pResourceMgr = NULL; pMaskCache = NULL; pVCacher = NULL; pSyncQuery = NULL; pSyncRTRes = NULL; pStateBlock = NULL; D3DC_INIT_SHADER_LIST(convolvePrograms, MAX_CONVOLVE); D3DC_INIT_SHADER_LIST(rescalePrograms, MAX_RESCALE); D3DC_INIT_SHADER_LIST(lookupPrograms, MAX_LOOKUP); D3DC_INIT_SHADER_LIST(basicGradPrograms, 4); D3DC_INIT_SHADER_LIST(linearGradPrograms, 8); D3DC_INIT_SHADER_LIST(radialGradPrograms, 8); pLCDGlyphCache= NULL; pGrayscaleGlyphCache= NULL; lcdTextProgram = NULL; aaPgramProgram = NULL; contextCaps = CAPS_EMPTY; bBeginScenePending = FALSE; ZeroMemory(&devCaps, sizeof(D3DCAPS9)); ZeroMemory(&curParams, sizeof(curParams)); extraAlpha = 1.0f; } void D3DContext::ReleaseDefPoolResources() { J2dTraceLn(J2D_TRACE_INFO, "D3DContext::ReleaseDefPoolResources"); EndScene(); contextCaps = CAPS_EMPTY; SAFE_RELEASE(pSyncQuery); SAFE_RELEASE(pStateBlock); if (pVCacher != NULL) { pVCacher->ReleaseDefPoolResources(); } if (pMaskCache != NULL) { pMaskCache->ReleaseDefPoolResources(); } if (pLCDGlyphCache != NULL) { pLCDGlyphCache->ReleaseDefPoolResources(); } if (pGrayscaleGlyphCache != NULL) { pGrayscaleGlyphCache->ReleaseDefPoolResources(); } if (pResourceMgr != NULL) { if (pSyncRTRes != NULL) { pResourceMgr->ReleaseResource(pSyncRTRes); pSyncRTRes = NULL; } pResourceMgr->ReleaseDefPoolResources(); } ZeroMemory(lastTexture, sizeof(lastTexture)); ZeroMemory(lastTextureColorState, sizeof(lastTextureColorState)); } void D3DContext::ReleaseContextResources() { J2dTraceLn1(J2D_TRACE_INFO, "D3DContext::ReleaseContextResources: pd3dDevice = 0x%x", pd3dDevice); ReleaseDefPoolResources(); // dispose shader lists ShaderList_Dispose(&convolvePrograms); ShaderList_Dispose(&rescalePrograms); ShaderList_Dispose(&lookupPrograms); ShaderList_Dispose(&basicGradPrograms); ShaderList_Dispose(&linearGradPrograms); ShaderList_Dispose(&radialGradPrograms); SAFE_DELETE(pLCDGlyphCache); SAFE_DELETE(pGrayscaleGlyphCache); SAFE_RELEASE(lcdTextProgram); SAFE_RELEASE(aaPgramProgram); SAFE_DELETE(pVCacher); SAFE_DELETE(pMaskCache); SAFE_DELETE(pResourceMgr); } D3DContext::~D3DContext() { J2dTraceLn2(J2D_TRACE_INFO, "~D3DContext: pd3dDevice=0x%x, pd3dObject =0x%x", pd3dDevice, pd3dObject); ReleaseContextResources(); SAFE_RELEASE(pd3dDevice); } HRESULT D3DContext::InitDevice(IDirect3DDevice9 *pd3dDevice) { HRESULT res = S_OK; pd3dDevice->GetDeviceCaps(&devCaps); J2dRlsTraceLn1(J2D_TRACE_INFO, "D3DContext::InitDevice: device %d", adapterOrdinal); // disable some of the unneeded and costly d3d functionality pd3dDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE); pd3dDevice->SetRenderState(D3DRS_SPECULARENABLE, FALSE); pd3dDevice->SetRenderState(D3DRS_LIGHTING, FALSE); pd3dDevice->SetRenderState(D3DRS_CLIPPING, FALSE); pd3dDevice->SetRenderState(D3DRS_ZENABLE, D3DZB_FALSE); pd3dDevice->SetRenderState(D3DRS_ZWRITEENABLE, D3DZB_FALSE); pd3dDevice->SetRenderState(D3DRS_COLORVERTEX, FALSE); pd3dDevice->SetRenderState(D3DRS_STENCILENABLE, FALSE); // set the default texture addressing mode pd3dDevice->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP); pd3dDevice->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP); // REMIND: check supported filters with // IDirect3D9::CheckDeviceFormat with D3DUSAGE_QUERY_FILTER pd3dDevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_POINT); pd3dDevice->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_POINT); // these states never change pd3dDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_MODULATE); pd3dDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE); pd3dDevice->SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE); pd3dDevice->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE); pd3dDevice->SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_MODULATE); pd3dDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_MODULATE); pd3dDevice->SetTextureStageState(1, D3DTSS_ALPHAARG2, D3DTA_CURRENT); pd3dDevice->SetTextureStageState(1, D3DTSS_COLORARG2, D3DTA_CURRENT); // init the array of latest textures ZeroMemory(lastTexture, sizeof(lastTexture)); ZeroMemory(lastTextureColorState, sizeof(lastTextureColorState)); opState = STATE_CHANGE; if (pResourceMgr == NULL) { res = D3DResourceManager::CreateInstance(this, &pResourceMgr); } else { res = pResourceMgr->Init(this); } RETURN_STATUS_IF_FAILED(res); if (pVCacher == NULL) { res = D3DVertexCacher::CreateInstance(this, &pVCacher); } else { res = pVCacher->Init(this); } RETURN_STATUS_IF_FAILED(res); if (pMaskCache == NULL) { res = D3DMaskCache::CreateInstance(this, &pMaskCache); } else{ res = pMaskCache->Init(this); } RETURN_STATUS_IF_FAILED(res); if (pLCDGlyphCache != NULL) { if (FAILED(res = pLCDGlyphCache->Init(this))) { // we can live without the cache SAFE_DELETE(pLCDGlyphCache); res = S_OK; } } if (pGrayscaleGlyphCache != NULL) { if (FAILED(res = pGrayscaleGlyphCache->Init(this))) { // we can live without the cache SAFE_DELETE(pGrayscaleGlyphCache); res = S_OK; } } D3DMATRIX tx; D3DUtils_SetIdentityMatrix(&tx); pd3dDevice->SetTransform(D3DTS_WORLD, &tx); bIsIdentityTx = TRUE; if (pSyncQuery == NULL) { // this is allowed to fail, do not propagate the error if (FAILED(pd3dDevice->CreateQuery(D3DQUERYTYPE_EVENT, &pSyncQuery))) { J2dRlsTraceLn(J2D_TRACE_WARNING, "D3DContext::InitDevice: sync query not available"); pSyncQuery = NULL; } } if (pSyncRTRes == NULL) { D3DFORMAT format; if (FAILED(GetResourceManager()-> CreateRTSurface(32, 32, TRUE, TRUE, &format, &pSyncRTRes))) { J2dRlsTraceLn(J2D_TRACE_WARNING, "D3DContext::InitDevice: " "error creating sync surface"); } } bBeginScenePending = FALSE; J2dRlsTraceLn1(J2D_TRACE_INFO, "D3DContext::InitDefice: successfully initialized device %d", adapterOrdinal); return res; } HRESULT D3DContext::CheckAndResetDevice() { HRESULT res = E_FAIL; J2dTraceLn(J2D_TRACE_INFO, "D3DContext::CheckAndResetDevice"); if (pd3dDevice != NULL) { if (FAILED(res = pd3dDevice->TestCooperativeLevel())) { if (res == D3DERR_DEVICELOST) { J2dTraceLn1(J2D_TRACE_VERBOSE, " device %d is still lost", adapterOrdinal); // nothing to be done here, wait for D3DERR_DEVICENOTRESET return res; } else if (res == D3DERR_DEVICENOTRESET) { J2dTraceLn1(J2D_TRACE_VERBOSE, " device %d needs to be reset", adapterOrdinal); res = ResetContext(); } else { // some unexpected error DebugPrintD3DError(res, "D3DContext::CheckAndResetDevice: "\ "unknown error %x from TestCooperativeLevel"); } } else { J2dTraceLn1(J2D_TRACE_VERBOSE, " device %d is not lost", adapterOrdinal); } } else { J2dTraceLn(J2D_TRACE_VERBOSE, " null device"); } return res; } HRESULT D3DContext::ResetContext() { HRESULT res = E_FAIL; J2dRlsTraceLn(J2D_TRACE_INFO, "D3DContext::ResetContext"); if (pd3dDevice != NULL) { D3DPRESENT_PARAMETERS newParams; newParams = curParams; if (newParams.Windowed) { // reset to the current display mode if we're windowed, // otherwise to the display mode we were in when the device // was lost newParams.BackBufferFormat = D3DFMT_UNKNOWN; newParams.FullScreen_RefreshRateInHz = 0; newParams.BackBufferWidth = 0; newParams.BackBufferHeight = 0; } res = ConfigureContext(&newParams); } return res; } HRESULT D3DContext::ConfigureContext(D3DPRESENT_PARAMETERS *pNewParams) { J2dRlsTraceLn1(J2D_TRACE_INFO, "D3DContext::ConfigureContext device %d", adapterOrdinal); HRESULT res = S_OK; D3DFORMAT stencilFormat; HWND focusHWND = D3DPipelineManager::GetInstance()->GetCurrentFocusWindow(); D3DDEVTYPE devType = D3DPipelineManager::GetInstance()->GetDeviceType(); // this is needed so that we can find the stencil buffer format if (pNewParams->BackBufferFormat == D3DFMT_UNKNOWN) { D3DDISPLAYMODE dm; pd3dObject->GetAdapterDisplayMode(adapterOrdinal, &dm); pNewParams->BackBufferFormat = dm.Format; } stencilFormat = D3DPipelineManager::GetInstance()->GetMatchingDepthStencilFormat( adapterOrdinal, pNewParams->BackBufferFormat, pNewParams->BackBufferFormat); pNewParams->EnableAutoDepthStencil = TRUE; pNewParams->AutoDepthStencilFormat = stencilFormat; // do not set device window in the windowed mode, we use additional // swap chains for rendering, the default chain is not used. otherwise // our scratch focus window will be made visible J2dTraceLn1(J2D_TRACE_VERBOSE, " windowed=%d",pNewParams->Windowed); if (pNewParams->Windowed) { pNewParams->hDeviceWindow = (HWND)0; } // The focus window may change when we're entering/exiting the full-screen // mode. It may either be set to the default focus window (when there are // no more devices in fs mode), or to fs window for another device // in fs mode. See D3DPipelineManager::GetCurrentFocusWindow. if (pd3dDevice != NULL) { D3DDEVICE_CREATION_PARAMETERS cParams; pd3dDevice->GetCreationParameters(&cParams); if (cParams.hFocusWindow != focusHWND) { J2dTraceLn(J2D_TRACE_VERBOSE, " focus window changed, need to recreate the device"); // if fs -> windowed, first exit fs, then recreate, otherwise // the screen might be left in a different display mode if (pNewParams->Windowed && !curParams.Windowed) { J2dTraceLn(J2D_TRACE_VERBOSE, " exiting full-screen mode, reset the device"); curParams.Windowed = FALSE; ReleaseDefPoolResources(); res = pd3dDevice->Reset(&curParams); if (FAILED(res)) { DebugPrintD3DError(res, "D3DContext::ConfigureContext: "\ "cound not reset the device"); } } // note that here we should release all device resources, not only // thos in the default pool since the device is released ReleaseContextResources(); SAFE_RELEASE(pd3dDevice); } } if (pd3dDevice != NULL) { J2dTraceLn(J2D_TRACE_VERBOSE, " resetting the device"); ReleaseDefPoolResources(); if (pNewParams->PresentationInterval == D3DPRESENT_INTERVAL_IMMEDIATE && !IsImmediateIntervalSupported()) { pNewParams->PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT; } res = pd3dDevice->Reset(pNewParams); if (FAILED(res)) { DebugPrintD3DError(res, "D3DContext::ConfigureContext: cound not reset the device"); return res; } J2dRlsTraceLn1(J2D_TRACE_INFO, "D3DContext::ConfigureContext: successfully reset device: %d", adapterOrdinal); } else { D3DCAPS9 d3dCaps; DWORD dwBehaviorFlags; J2dTraceLn(J2D_TRACE_VERBOSE, " creating a new device"); if (FAILED(res = pd3dObject->GetDeviceCaps(adapterOrdinal, devType, &d3dCaps))) { DebugPrintD3DError(res, "D3DContext::ConfigureContext: failed to get caps"); return res; } if (pNewParams->PresentationInterval == D3DPRESENT_INTERVAL_IMMEDIATE && !(d3dCaps.PresentationIntervals & D3DPRESENT_INTERVAL_IMMEDIATE)) { pNewParams->PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT; } // not preserving fpu control word could cause issues (4860749) dwBehaviorFlags = D3DCREATE_FPU_PRESERVE; J2dRlsTrace(J2D_TRACE_VERBOSE, "[V] dwBehaviorFlags=D3DCREATE_FPU_PRESERVE|"); if (d3dCaps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT) { J2dRlsTrace(J2D_TRACE_VERBOSE, "D3DCREATE_HARDWARE_VERTEXPROCESSING"); dwBehaviorFlags |= D3DCREATE_HARDWARE_VERTEXPROCESSING; } else { J2dRlsTrace(J2D_TRACE_VERBOSE, "D3DCREATE_SOFTWARE_VERTEXPROCESSING"); dwBehaviorFlags |= D3DCREATE_SOFTWARE_VERTEXPROCESSING; } // Handling focus changes by ourselves proved to be problematic, // so we're reverting back to D3D handling // dwBehaviorFlags |= D3DCREATE_NOWINDOWCHANGES; J2dRlsTrace(J2D_TRACE_VERBOSE,"\n"); if (FAILED(res = pd3dObject->CreateDevice(adapterOrdinal, devType, focusHWND, dwBehaviorFlags, pNewParams, &pd3dDevice))) { DebugPrintD3DError(res, "D3DContext::ConfigureContext: error creating d3d device"); return res; } J2dRlsTraceLn1(J2D_TRACE_INFO, "D3DContext::ConfigureContext: successfully created device: %d", adapterOrdinal); bIsHWRasterizer = (devType == D3DDEVTYPE_HAL); } curParams = *pNewParams; // during the creation of the device d3d modifies this field, we reset // it back to 0 curParams.Flags = 0; if (FAILED(res = InitDevice(pd3dDevice))) { ReleaseContextResources(); return res; } res = InitContextCaps(); return res; } HRESULT D3DContext::InitContext() { J2dRlsTraceLn1(J2D_TRACE_INFO, "D3DContext::InitContext device %d", adapterOrdinal); D3DPRESENT_PARAMETERS params; ZeroMemory(¶ms, sizeof(D3DPRESENT_PARAMETERS)); params.hDeviceWindow = 0; params.Windowed = TRUE; params.BackBufferCount = 1; params.BackBufferFormat = D3DFMT_UNKNOWN; params.SwapEffect = D3DSWAPEFFECT_DISCARD; params.PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT; return ConfigureContext(¶ms); } HRESULT D3DContext::Sync() { HRESULT res = S_OK; J2dTraceLn(J2D_TRACE_INFO, "D3DContext::Sync"); if (pSyncQuery != NULL) { J2dTrace(J2D_TRACE_VERBOSE, " flushing the device queue.."); while (S_FALSE == (res = pSyncQuery->GetData(NULL, 0, D3DGETDATA_FLUSH))) ; J2dTrace(J2D_TRACE_VERBOSE, ".. done\n"); } if (pSyncRTRes != NULL) { D3DLOCKED_RECT lr; IDirect3DSurface9 *pSurface = pSyncRTRes->GetSurface(); if (SUCCEEDED(pSurface->LockRect(&lr, NULL, D3DLOCK_NOSYSLOCK))) { pSurface->UnlockRect(); } } return res; } HRESULT D3DContext::SaveState() { HRESULT res; RETURN_STATUS_IF_NULL(pd3dDevice, S_OK); J2dTraceLn(J2D_TRACE_INFO, "D3DContext::SaveState"); FlushVertexQueue(); UpdateState(STATE_CHANGE); if (pStateBlock != NULL) { J2dTraceLn(J2D_TRACE_WARNING, "D3DContext::SaveState: existing state block!"); SAFE_RELEASE(pStateBlock); } if (SUCCEEDED(res = pd3dDevice->CreateStateBlock(D3DSBT_ALL, &pStateBlock))) { J2dTraceLn(J2D_TRACE_VERBOSE, " created state block"); } else { J2dTraceLn(J2D_TRACE_WARNING, "D3DContext::SaveState: failed to create state block"); } ZeroMemory(lastTexture, sizeof(lastTexture)); return res; } HRESULT D3DContext::RestoreState() { HRESULT res = S_OK; J2dTraceLn(J2D_TRACE_INFO, "D3DContext::RestoreState"); FlushVertexQueue(); UpdateState(STATE_CHANGE); if (pStateBlock != NULL) { if (SUCCEEDED(res = pStateBlock->Apply())) { J2dTraceLn(J2D_TRACE_VERBOSE, " restored device state"); } else { J2dTraceLn(J2D_TRACE_WARNING, "D3DContext::RestoreState: failed to restore state"); } SAFE_RELEASE(pStateBlock); } else { J2dTraceLn(J2D_TRACE_WARNING, "D3DContext::RestoreState: empty state block!"); } ZeroMemory(lastTexture, sizeof(lastTexture)); return res; } #define POINT_FILTER_CAP (D3DPTFILTERCAPS_MAGFPOINT|D3DPTFILTERCAPS_MINFPOINT) #define LINEAR_FILTER_CAP (D3DPTFILTERCAPS_MAGFLINEAR|D3DPTFILTERCAPS_MINFLINEAR) BOOL D3DContext::IsStretchRectFilteringSupported(D3DTEXTUREFILTERTYPE fType) { if (fType == D3DTEXF_POINT) { return ((devCaps.StretchRectFilterCaps & POINT_FILTER_CAP) != 0); } if (fType == D3DTEXF_LINEAR) { return ((devCaps.StretchRectFilterCaps & LINEAR_FILTER_CAP) != 0); } return FALSE; } BOOL D3DContext::IsTextureFilteringSupported(D3DTEXTUREFILTERTYPE fType) { if (fType == D3DTEXF_POINT) { return ((devCaps.TextureFilterCaps & POINT_FILTER_CAP) != 0); } if (fType == D3DTEXF_LINEAR) { return ((devCaps.TextureFilterCaps & LINEAR_FILTER_CAP) != 0); } return FALSE; } BOOL D3DContext::IsTextureFormatSupported(D3DFORMAT format, DWORD usage) { HRESULT hr = pd3dObject->CheckDeviceFormat(adapterOrdinal, devCaps.DeviceType, curParams.BackBufferFormat, usage, D3DRTYPE_TEXTURE, format); return SUCCEEDED( hr ); } BOOL D3DContext::IsDepthStencilBufferOk(D3DSURFACE_DESC *pTargetDesc) { IDirect3DSurface9 *pStencil; J2dTraceLn(J2D_TRACE_INFO, "D3DContext::IsDepthStencilBufferOk"); if (SUCCEEDED(pd3dDevice->GetDepthStencilSurface(&pStencil))) { D3DSURFACE_DESC descStencil; pStencil->GetDesc(&descStencil); pStencil->Release(); D3DDISPLAYMODE dm; return (SUCCEEDED(pd3dDevice->GetDisplayMode(0, &dm)) && pTargetDesc->Width <= descStencil.Width && pTargetDesc->Height <= descStencil.Height && SUCCEEDED(pd3dObject->CheckDepthStencilMatch( adapterOrdinal, devCaps.DeviceType, dm.Format, pTargetDesc->Format, descStencil.Format))); } J2dTraceLn(J2D_TRACE_VERBOSE, " current stencil buffer is not compatible with new Render Target"); return false; } HRESULT D3DContext::InitDepthStencilBuffer(D3DSURFACE_DESC *pTargetDesc) { HRESULT res; IDirect3DSurface9 *pBB; D3DDISPLAYMODE dm; J2dTraceLn(J2D_TRACE_INFO, "D3DContext::InitDepthStencilBuffer"); if (FAILED(res = pd3dDevice->GetDisplayMode(0, &dm))) { return res; } D3DFORMAT newFormat = D3DPipelineManager::GetInstance()->GetMatchingDepthStencilFormat( adapterOrdinal, dm.Format, pTargetDesc->Format); res = pd3dDevice->CreateDepthStencilSurface( pTargetDesc->Width, pTargetDesc->Height, newFormat, D3DMULTISAMPLE_NONE, 0, false, &pBB, 0); if (SUCCEEDED(res)) { res = pd3dDevice->SetDepthStencilSurface(pBB); pBB->Release(); } return res; } HRESULT D3DContext::SetRenderTarget(IDirect3DSurface9 *pSurface) { static D3DMATRIX tx; HRESULT res; D3DSURFACE_DESC descNew; IDirect3DSurface9 *pCurrentTarget; J2dTraceLn1(J2D_TRACE_INFO, "D3DContext::SetRenderTarget: pSurface=0x%x", pSurface); RETURN_STATUS_IF_NULL(pd3dDevice, E_FAIL); RETURN_STATUS_IF_NULL(pSurface, E_FAIL); pSurface->GetDesc(&descNew); if (SUCCEEDED(res = pd3dDevice->GetRenderTarget(0, &pCurrentTarget))) { if (pCurrentTarget != pSurface) { FlushVertexQueue(); if (FAILED(res = pd3dDevice->SetRenderTarget(0, pSurface))) { DebugPrintD3DError(res, "D3DContext::SetRenderTarget: "\ "error setting render target"); SAFE_RELEASE(pCurrentTarget); return res; } if (!IsDepthStencilBufferOk(&descNew)) { if (FAILED(res = InitDepthStencilBuffer(&descNew))) { SAFE_RELEASE(pCurrentTarget); return res; } } } SAFE_RELEASE(pCurrentTarget); } // we set the transform even if the render target didn't change; // this is because in some cases (fs mode) we use the default SwapChain of // the device, and its render target will be the same as the device's, and // we have to set the matrix correctly. This shouldn't be a performance // issue as render target changes are relatively rare D3DUtils_SetOrthoMatrixOffCenterLH(&tx, (float)descNew.Width, (float)descNew.Height); pd3dDevice->SetTransform(D3DTS_PROJECTION, &tx); J2dTraceLn1(J2D_TRACE_VERBOSE, " current render target=0x%x", pSurface); return res; } HRESULT D3DContext::ResetTransform() { HRESULT res = S_OK; D3DMATRIX tx; J2dTraceLn(J2D_TRACE_INFO, "D3DContext::ResetTransform"); if (pd3dDevice == NULL) { return E_FAIL; } // no need for state change, just flush the queue FlushVertexQueue(); D3DUtils_SetIdentityMatrix(&tx); if (FAILED(res = pd3dDevice->SetTransform(D3DTS_WORLD, &tx))) { DebugPrintD3DError(res, "D3DContext::SetTransform failed"); } bIsIdentityTx = TRUE; return res; } HRESULT D3DContext::SetTransform(jdouble m00, jdouble m10, jdouble m01, jdouble m11, jdouble m02, jdouble m12) { HRESULT res = S_OK; D3DMATRIX tx, tx1; J2dTraceLn(J2D_TRACE_INFO, "D3DContext::SetTransform"); if (pd3dDevice == NULL) { return E_FAIL; } // no need for state change, just flush the queue FlushVertexQueue(); // In order to correctly map texels to pixels we need to // adjust geometry by -0.5f in the transformed space. // In order to do that we first create a translated matrix // and then concatenate it with the world transform. // // Note that we only use non-id transform with DrawTexture, // the rest is rendered pre-transformed. // // The identity transform for textures is handled in // D3DVertexCacher::DrawTexture() because shifting by -0.5 for id // transform breaks lines rendering. ZeroMemory(&tx1, sizeof(D3DMATRIX)); tx1._11 = (float)m00; tx1._12 = (float)m10; tx1._21 = (float)m01; tx1._22 = (float)m11; tx1._41 = (float)m02; tx1._42 = (float)m12; tx1._33 = 1.0f; tx1._44 = 1.0f; D3DUtils_SetIdentityMatrix(&tx); tx._41 = -0.5f; tx._42 = -0.5f; D3DUtils_2DConcatenateM(&tx, &tx1); J2dTraceLn4(J2D_TRACE_VERBOSE, " %5f %5f %5f %5f", tx._11, tx._12, tx._13, tx._14); J2dTraceLn4(J2D_TRACE_VERBOSE, " %5f %5f %5f %5f", tx._21, tx._22, tx._23, tx._24); J2dTraceLn4(J2D_TRACE_VERBOSE, " %5f %5f %5f %5f", tx._31, tx._32, tx._33, tx._34); J2dTraceLn4(J2D_TRACE_VERBOSE, " %5f %5f %5f %5f", tx._41, tx._42, tx._43, tx._44); if (FAILED(res = pd3dDevice->SetTransform(D3DTS_WORLD, &tx))) { DebugPrintD3DError(res, "D3DContext::SetTransform failed"); } bIsIdentityTx = FALSE; return res; } HRESULT D3DContext::SetRectClip(int x1, int y1, int x2, int y2) { HRESULT res = S_OK; D3DSURFACE_DESC desc; IDirect3DSurface9 *pCurrentTarget; J2dTraceLn(J2D_TRACE_INFO, "D3DContext::SetRectClip"); J2dTraceLn4(J2D_TRACE_VERBOSE, " x1=%-4d y1=%-4d x2=%-4d y2=%-4d", x1, y1, x2, y2); RETURN_STATUS_IF_NULL(pd3dDevice, E_FAIL); // no need for state change, just flush the queue FlushVertexQueue(); pd3dDevice->SetRenderState(D3DRS_ZENABLE, D3DZB_FALSE); res = pd3dDevice->GetRenderTarget(0, &pCurrentTarget); RETURN_STATUS_IF_FAILED(res); pCurrentTarget->GetDesc(&desc); SAFE_RELEASE(pCurrentTarget); if (x1 <= 0 && y1 <= 0 && (UINT)x2 >= desc.Width && (UINT)y2 >= desc.Height) { J2dTraceLn(J2D_TRACE_VERBOSE, " disabling clip (== render target dimensions)"); return pd3dDevice->SetRenderState(D3DRS_SCISSORTESTENABLE, FALSE); } // clip to the dimensions of the target surface, otherwise // SetScissorRect will fail if (x1 < 0) x1 = 0; if (y1 < 0) y1 = 0; if ((UINT)x2 > desc.Width) x2 = desc.Width; if ((UINT)y2 > desc.Height) y2 = desc.Height; if (x1 > x2) x2 = x1 = 0; if (y1 > y2) y2 = y1 = 0; RECT newRect = { x1, y1, x2, y2 }; if (SUCCEEDED(res = pd3dDevice->SetScissorRect(&newRect))) { res = pd3dDevice->SetRenderState(D3DRS_SCISSORTESTENABLE, TRUE); } else { DebugPrintD3DError(res, "Error setting scissor rect"); J2dRlsTraceLn4(J2D_TRACE_ERROR, " x1=%-4d y1=%-4d x2=%-4d y2=%-4d", x1, y1, x2, y2); } return res; } HRESULT D3DContext::ResetClip() { J2dTraceLn(J2D_TRACE_INFO, "D3DContext::ResetClip"); // no need for state change, just flush the queue FlushVertexQueue(); pd3dDevice->SetRenderState(D3DRS_SCISSORTESTENABLE, FALSE); return pd3dDevice->SetRenderState(D3DRS_ZENABLE, D3DZB_FALSE); } ClipType D3DContext::GetClipType() { // REMIND: this method could be optimized: we could keep the // clip state around when re/setting the clip instead of asking // every time. DWORD zEnabled = 0; DWORD stEnabled = 0; J2dTraceLn(J2D_TRACE_INFO, "D3DContext::GetClipType"); pd3dDevice->GetRenderState(D3DRS_SCISSORTESTENABLE, &stEnabled); if (stEnabled) { return CLIP_RECT; } pd3dDevice->GetRenderState(D3DRS_ZENABLE, &zEnabled); if (zEnabled) { return CLIP_SHAPE; } return CLIP_NONE; } /** * This method assumes that ::SetRenderTarget has already * been called. SetRenderTarget creates and attaches a * depth buffer to the target surface prior to setting it * as target surface to the device. */ DWORD dwAlphaSt, dwSrcBlendSt, dwDestBlendSt; D3DMATRIX tx, idTx; HRESULT D3DContext::BeginShapeClip() { HRESULT res = S_OK; J2dTraceLn(J2D_TRACE_INFO, "D3DContext::BeginShapeClip"); UpdateState(STATE_CHANGE); pd3dDevice->SetRenderState(D3DRS_SCISSORTESTENABLE, FALSE); // save alpha blending state pd3dDevice->GetRenderState(D3DRS_ALPHABLENDENABLE, &dwAlphaSt); pd3dDevice->GetRenderState(D3DRS_SRCBLEND, &dwSrcBlendSt); pd3dDevice->GetRenderState(D3DRS_DESTBLEND, &dwDestBlendSt); pd3dDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE); pd3dDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ZERO); pd3dDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ONE); pd3dDevice->GetTransform(D3DTS_WORLD, &tx); D3DUtils_SetIdentityMatrix(&idTx); // translate the clip spans by 1.0f in z direction so that the // clip spans are rendered to the z buffer idTx._43 = 1.0f; pd3dDevice->SetTransform(D3DTS_WORLD, &idTx); // The depth buffer is first cleared with zeroes, which is the farthest // plane from the viewer (our projection matrix is an inversed orthogonal // transform). // To set the clip we'll render the clip spans with Z coordinates of 1.0f // (the closest to the viewer). Since all rendering primitives // have their vertices' Z coordinate set to 0.0, they will effectively be // clipped because the Z depth test for them will fail (vertex with 1.0 // depth is closer than the one with 0.0f) pd3dDevice->SetRenderState(D3DRS_ZENABLE, D3DZB_TRUE); pd3dDevice->SetRenderState(D3DRS_ZWRITEENABLE, TRUE); pd3dDevice->SetRenderState(D3DRS_ZFUNC, D3DCMP_ALWAYS); pd3dDevice->Clear(0, NULL, D3DCLEAR_ZBUFFER, 0L, 0.0f, 0x0L); //res = BeginScene(STATE_SHAPE_CLIPOP); return res; } HRESULT D3DContext::EndShapeClip() { HRESULT res; // no need for state change, just flush the queue res = FlushVertexQueue(); // restore alpha blending state pd3dDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, dwAlphaSt); pd3dDevice->SetRenderState(D3DRS_SRCBLEND, dwSrcBlendSt); pd3dDevice->SetRenderState(D3DRS_DESTBLEND, dwDestBlendSt); // resore the transform pd3dDevice->SetTransform(D3DTS_WORLD, &tx); // Enable the depth buffer. // We disable further updates to the depth buffer: it should only // be updated in SetClip method. pd3dDevice->SetRenderState(D3DRS_ZWRITEENABLE, FALSE); pd3dDevice->SetRenderState(D3DRS_ZFUNC, D3DCMP_LESS); return res; } HRESULT D3DContext::UploadTileToTexture(D3DResource *pTextureRes, void *pixels, jint dstx, jint dsty, jint srcx, jint srcy, jint srcWidth, jint srcHeight, jint srcStride, TileFormat srcFormat, jint *pPixelsTouchedL, jint* pPixelsTouchedR) { #ifndef PtrAddBytes #define PtrAddBytes(p, b) ((void *) (((intptr_t) (p)) + (b))) #define PtrCoord(p, x, xinc, y, yinc) PtrAddBytes(p, (y)*(yinc) + (x)*(xinc)) #endif // PtrAddBytes HRESULT res = S_OK; IDirect3DTexture9 *pTexture = pTextureRes->GetTexture(); D3DSURFACE_DESC *pDesc = pTextureRes->GetDesc(); RECT r = { dstx, dsty, dstx+srcWidth, dsty+srcHeight }; RECT *pR = &r; D3DLOCKED_RECT lockedRect; DWORD dwLockFlags = D3DLOCK_NOSYSLOCK; // these are only counted for LCD glyph uploads jint pixelsTouchedL = 0, pixelsTouchedR = 0; J2dTraceLn(J2D_TRACE_INFO, "D3DContext::UploadTileToTexture"); J2dTraceLn4(J2D_TRACE_VERBOSE, " rect={%-4d, %-4d, %-4d, %-4d}", r.left, r.top, r.right, r.bottom); if (pDesc->Usage == D3DUSAGE_DYNAMIC) { // it is safe to lock with discard because we don't care about the // contents of dynamic textures and dstx,dsty for this case is // always 0,0 because we are uploading into a tile texture dwLockFlags |= D3DLOCK_DISCARD; pR = NULL; } if (FAILED(res = pTexture->LockRect(0, &lockedRect, pR, dwLockFlags))) { DebugPrintD3DError(res, "D3DContext::UploadImageToTexture: could "\ "not lock texture"); return res; } if (srcFormat == TILEFMT_1BYTE_ALPHA) { // either a MaskFill tile, or a grayscale glyph if (pDesc->Format == D3DFMT_A8) { void *pSrcPixels = PtrCoord(pixels, srcx, 1, srcy, srcStride); void *pDstPixels = lockedRect.pBits; do { memcpy(pDstPixels, pSrcPixels, srcWidth); pSrcPixels = PtrAddBytes(pSrcPixels, srcStride); pDstPixels = PtrAddBytes(pDstPixels, lockedRect.Pitch); } while (--srcHeight > 0); } else if (pDesc->Format == D3DFMT_A8R8G8B8) { jubyte *pSrcPixels = (jubyte*) PtrCoord(pixels, srcx, 1, srcy, srcStride); jint *pDstPixels = (jint*)lockedRect.pBits; for (int yy = 0; yy < srcHeight; yy++) { for (int xx = 0; xx < srcWidth; xx++) { // only need to set the alpha channel (the D3D texture // state will be setup in this case to replicate the // alpha channel as needed) pDstPixels[xx] = pSrcPixels[xx] << 24; } pSrcPixels = (jubyte*)PtrAddBytes(pSrcPixels, srcStride); pDstPixels = (jint*)PtrAddBytes(pDstPixels, lockedRect.Pitch); } } } else if (srcFormat == TILEFMT_3BYTE_RGB) { // LCD glyph with RGB order if (pDesc->Format == D3DFMT_R8G8B8) { jubyte *pSrcPixels = (jubyte*) PtrCoord(pixels, srcx, 3, srcy, srcStride); jubyte *pDstPixels = (jubyte*)lockedRect.pBits; for (int yy = 0; yy < srcHeight; yy++) { for (int xx = 0; xx < srcWidth*3; xx+=3) { // alpha channel is ignored in this case // (note that this is backwards from what one might // expect; it appears that D3DFMT_R8G8B8 is actually // laid out in BGR order in memory) pDstPixels[xx+0] = pSrcPixels[xx+2]; pDstPixels[xx+1] = pSrcPixels[xx+1]; pDstPixels[xx+2] = pSrcPixels[xx+0]; } pixelsTouchedL += (pDstPixels[0+0]|pDstPixels[0+1]|pDstPixels[0+2]) ? 1 : 0; jint i = 3*(srcWidth-1); pixelsTouchedR += (pDstPixels[i+0]|pDstPixels[i+1]|pDstPixels[i+2]) ? 1 : 0; pSrcPixels = (jubyte*)PtrAddBytes(pSrcPixels, srcStride); pDstPixels = (jubyte*)PtrAddBytes(pDstPixels, lockedRect.Pitch); } } else if (pDesc->Format == D3DFMT_A8R8G8B8) { jubyte *pSrcPixels = (jubyte*) PtrCoord(pixels, srcx, 3, srcy, srcStride); jint *pDstPixels = (jint*)lockedRect.pBits; for (int yy = 0; yy < srcHeight; yy++) { for (int dx = 0, sx = 0; dx < srcWidth; dx++, sx+=3) { // alpha channel is ignored in this case jubyte r = pSrcPixels[sx+0]; jubyte g = pSrcPixels[sx+1]; jubyte b = pSrcPixels[sx+2]; pDstPixels[dx] = (r << 16) | (g << 8) | (b); } pixelsTouchedL += (pDstPixels[0] ? 1 : 0); pixelsTouchedR += (pDstPixels[srcWidth-1] ? 1 : 0); pSrcPixels = (jubyte*)PtrAddBytes(pSrcPixels, srcStride); pDstPixels = (jint*)PtrAddBytes(pDstPixels, lockedRect.Pitch); } } } else if (srcFormat == TILEFMT_3BYTE_BGR) { // LCD glyph with BGR order if (pDesc->Format == D3DFMT_R8G8B8) { void *pSrcPixels = PtrCoord(pixels, srcx, 3, srcy, srcStride); void *pDstPixels = lockedRect.pBits; jubyte *pbDst; do { // alpha channel is ignored in this case // (note that this is backwards from what one might // expect; it appears that D3DFMT_R8G8B8 is actually // laid out in BGR order in memory) memcpy(pDstPixels, pSrcPixels, srcWidth * 3); pbDst = (jubyte*)pDstPixels; pixelsTouchedL +=(pbDst[0+0]|pbDst[0+1]|pbDst[0+2]) ? 1 : 0; jint i = 3*(srcWidth-1); pixelsTouchedR +=(pbDst[i+0]|pbDst[i+1]|pbDst[i+2]) ? 1 : 0; pSrcPixels = PtrAddBytes(pSrcPixels, srcStride); pDstPixels = PtrAddBytes(pDstPixels, lockedRect.Pitch); } while (--srcHeight > 0); } else if (pDesc->Format == D3DFMT_A8R8G8B8) { jubyte *pSrcPixels = (jubyte*) PtrCoord(pixels, srcx, 3, srcy, srcStride); jint *pDstPixels = (jint*)lockedRect.pBits; for (int yy = 0; yy < srcHeight; yy++) { for (int dx = 0, sx = 0; dx < srcWidth; dx++, sx+=3) { // alpha channel is ignored in this case jubyte b = pSrcPixels[sx+0]; jubyte g = pSrcPixels[sx+1]; jubyte r = pSrcPixels[sx+2]; pDstPixels[dx] = (r << 16) | (g << 8) | (b); } pixelsTouchedL += (pDstPixels[0] ? 1 : 0); pixelsTouchedR += (pDstPixels[srcWidth-1] ? 1 : 0); pSrcPixels = (jubyte*)PtrAddBytes(pSrcPixels, srcStride); pDstPixels = (jint*)PtrAddBytes(pDstPixels, lockedRect.Pitch); } } } else if (srcFormat == TILEFMT_4BYTE_ARGB_PRE) { // MaskBlit tile if (pDesc->Format == D3DFMT_A8R8G8B8) { void *pSrcPixels = PtrCoord(pixels, srcx, 4, srcy, srcStride); void *pDstPixels = lockedRect.pBits; do { memcpy(pDstPixels, pSrcPixels, srcWidth * 4); pSrcPixels = PtrAddBytes(pSrcPixels, srcStride); pDstPixels = PtrAddBytes(pDstPixels, lockedRect.Pitch); } while (--srcHeight > 0); } } else { // should not happen, no-op just in case... } if (pPixelsTouchedL) { *pPixelsTouchedL = pixelsTouchedL; } if (pPixelsTouchedR) { *pPixelsTouchedR = pixelsTouchedR; } return pTexture->UnlockRect(0); } HRESULT D3DContext::InitLCDGlyphCache() { if (pLCDGlyphCache == NULL) { return D3DGlyphCache::CreateInstance(this, CACHE_LCD, &pLCDGlyphCache); } return S_OK; } HRESULT D3DContext::InitGrayscaleGlyphCache() { if (pGrayscaleGlyphCache == NULL) { return D3DGlyphCache::CreateInstance(this, CACHE_GRAY, &pGrayscaleGlyphCache); } return S_OK; } HRESULT D3DContext::ResetComposite() { J2dTraceLn(J2D_TRACE_INFO, "D3DContext::ResetComposite"); RETURN_STATUS_IF_NULL(pd3dDevice, E_FAIL); HRESULT res = UpdateState(STATE_CHANGE); pd3dDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE); extraAlpha = 1.0f; return res; } HRESULT D3DContext::SetAlphaComposite(jint rule, jfloat ea, jint flags) { HRESULT res; J2dTraceLn3(J2D_TRACE_INFO, "D3DContext::SetAlphaComposite: rule=%-1d ea=%f flags=%d", rule, ea, flags); RETURN_STATUS_IF_NULL(pd3dDevice, E_FAIL); res = UpdateState(STATE_CHANGE); // we can safely disable blending when: // - comp is SrcNoEa or SrcOverNoEa, and // - the source is opaque // (turning off blending can have a large positive impact on performance) if ((rule == RULE_Src || rule == RULE_SrcOver) && (ea == 1.0f) && (flags & D3DC_SRC_IS_OPAQUE)) { J2dTraceLn1(J2D_TRACE_VERBOSE, " disabling alpha comp rule=%-1d ea=1.0 src=opq)", rule); pd3dDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE); } else { J2dTraceLn2(J2D_TRACE_VERBOSE, " enabling alpha comp (rule=%-1d ea=%f)", rule, ea); pd3dDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE); pd3dDevice->SetRenderState(D3DRS_SRCBLEND, StdBlendRules[rule].src); pd3dDevice->SetRenderState(D3DRS_DESTBLEND, StdBlendRules[rule].dst); } extraAlpha = ea; return res; } #ifdef UPDATE_TX // Note: this method of adjusting pixel to texel mapping proved to be // difficult to perfect. The current variation works great for id, // scale (including all kinds of flips) transforms, but not still not // for generic transforms. // // Since we currently only do DrawTexture with non-id transform we instead // adjust the geometry (see D3DVertexCacher::DrawTexture(), SetTransform()) // // In order to enable this code path UpdateTextureTransforms needs to // be called in SetTexture(), SetTransform() and ResetTranform(). HRESULT D3DContext::UpdateTextureTransforms(DWORD dwSamplerToUpdate) { HRESULT res = S_OK; DWORD dwSampler, dwMaxSampler; if (dwSamplerToUpdate == -1) { // update all used samplers, dwMaxSampler will be set to max dwSampler = 0; dwSampler = MAX_USED_TEXTURE_SAMPLER; J2dTraceLn(J2D_TRACE_INFO, "D3DContext::UpdateTextureTransforms: "\ "updating all samplers"); } else { // update only given sampler, dwMaxSampler will be set to it as well dwSampler = dwSamplerToUpdate; dwMaxSampler = dwSamplerToUpdate; J2dTraceLn1(J2D_TRACE_INFO, "D3DContext::UpdateTextureTransforms: "\ "updating sampler %d", dwSampler); } do { D3DTRANSFORMSTATETYPE state = (D3DTRANSFORMSTATETYPE)(D3DTS_TEXTURE0 + dwSampler); IDirect3DTexture9 *pTexture = lastTexture[dwSampler]; if (pTexture != NULL) { D3DMATRIX mt, tx; D3DSURFACE_DESC texDesc; pd3dDevice->GetTransform(D3DTS_WORLD, &tx); J2dTraceLn4(10, " %5f %5f %5f %5f", tx._11, tx._12, tx._13, tx._14); J2dTraceLn4(10, " %5f %5f %5f %5f", tx._21, tx._22, tx._23, tx._24); J2dTraceLn4(10, " %5f %5f %5f %5f", tx._31, tx._32, tx._33, tx._34); J2dTraceLn4(10, " %5f %5f %5f %5f", tx._41, tx._42, tx._43, tx._44); // this formula works for scales and flips if (tx._11 == 0.0f) { tx._11 = tx._12; } if (tx._22 == 0.0f) { tx._22 = tx._21; } pTexture->GetLevelDesc(0, &texDesc); // shift by .5 texel, but take into account // the scale factor of the device transform // REMIND: this approach is not entirely correct, // as it only takes into account the scale of the device // transform. mt._31 = (1.0f / (2.0f * texDesc.Width * tx._11)); mt._32 = (1.0f / (2.0f * texDesc.Height * tx._22)); J2dTraceLn2(J2D_TRACE_VERBOSE, " offsets: tx=%f ty=%f", mt._31, mt._32); pd3dDevice->SetTextureStageState(dwSampler, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_COUNT2); res = pd3dDevice->SetTransform(state, &mt); } else { res = pd3dDevice->SetTextureStageState(dwSampler, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_DISABLE); } dwSampler++; } while (dwSampler <= dwMaxSampler); return res; } #endif // UPDATE_TX /** * We go into the pains of maintaining the list of set textures * instead of just calling GetTexture() and comparing the old one * with the new one because it's actually noticeably slower to call * GetTexture() (note that we'd have to then call Release() on the * texture since GetTexture() increases texture's ref. count). */ HRESULT D3DContext::SetTexture(IDirect3DTexture9 *pTexture, DWORD dwSampler) { HRESULT res = S_OK; J2dTraceLn(J2D_TRACE_INFO, "D3DContext::SetTexture"); if (dwSampler < 0 || dwSampler > MAX_USED_TEXTURE_SAMPLER) { J2dTraceLn1(J2D_TRACE_ERROR, "D3DContext::SetTexture: incorrect sampler: %d", dwSampler); return E_FAIL; } if (lastTexture[dwSampler] != pTexture) { if (FAILED(res = FlushVertexQueue())) { return res; } J2dTraceLn2(J2D_TRACE_VERBOSE, " new texture=0x%x on sampler %d", pTexture, dwSampler); res = pd3dDevice->SetTexture(dwSampler, pTexture); if (SUCCEEDED(res)) { lastTexture[dwSampler] = pTexture; // REMIND: see comment at UpdateTextureTransforms #ifdef UPDATE_TX res = UpdateTextureTransforms(dwSampler); #endif } else { lastTexture[dwSampler] = NULL; } } return res; } HRESULT D3DContext::UpdateTextureColorState(DWORD dwState, DWORD dwSampler) { HRESULT res = S_OK; if (dwState != lastTextureColorState[dwSampler]) { res = pd3dDevice->SetTextureStageState(dwSampler, D3DTSS_ALPHAARG1, dwState); res = pd3dDevice->SetTextureStageState(dwSampler, D3DTSS_COLORARG1, dwState); lastTextureColorState[dwSampler] = dwState; } return res; } HRESULT /*NOLOCK*/ D3DContext::UpdateState(jbyte newState) { HRESULT res = S_OK; if (opState == newState) { // The op is the same as last time, so we can return immediately. return res; } else if (opState != STATE_CHANGE) { res = FlushVertexQueue(); } switch (opState) { case STATE_MASKOP: pMaskCache->Disable(); break; case STATE_GLYPHOP: D3DTR_DisableGlyphVertexCache(this); break; case STATE_TEXTUREOP: // optimization: certain state changes (those marked STATE_CHANGE) // are allowed while texturing is enabled. // In this case, we can allow previousOp to remain as it is and // then return early. if (newState == STATE_CHANGE) { return res; } // REMIND: not necessary if we are switching to MASKOP or GLYPHOP // (or a complex paint, for that matter), but would that be a // worthwhile optimization? SetTexture(NULL); break; case STATE_AAPGRAMOP: res = DisableAAParallelogramProgram(); break; default: break; } switch (newState) { case STATE_MASKOP: pMaskCache->Enable(); UpdateTextureColorState(D3DTA_TEXTURE | D3DTA_ALPHAREPLICATE); break; case STATE_GLYPHOP: D3DTR_EnableGlyphVertexCache(this); UpdateTextureColorState(D3DTA_TEXTURE | D3DTA_ALPHAREPLICATE); break; case STATE_TEXTUREOP: UpdateTextureColorState(D3DTA_TEXTURE); break; case STATE_AAPGRAMOP: res = EnableAAParallelogramProgram(); break; default: break; } opState = newState; return res; } HRESULT D3DContext::FlushVertexQueue() { if (pVCacher != NULL) { return pVCacher->Render(); } return E_FAIL; } HRESULT D3DContext::BeginScene(jbyte newState) { if (!pd3dDevice) { return E_FAIL; } else { UpdateState(newState); if (!bBeginScenePending) { bBeginScenePending = TRUE; HRESULT res = pd3dDevice->BeginScene(); J2dTraceLn(J2D_TRACE_INFO, "D3DContext::BeginScene"); if (FAILED(res)) { // this will cause context reinitialization opState = STATE_CHANGE; } return res; } return S_OK; } } HRESULT D3DContext::EndScene() { if (bBeginScenePending) { FlushVertexQueue(); bBeginScenePending = FALSE; J2dTraceLn(J2D_TRACE_INFO, "D3DContext::EndScene"); return pd3dDevice->EndScene(); } return S_OK; } /** * Compiles and links the given fragment shader program. If * successful, this function returns a handle to the newly created shader * program; otherwise returns 0. */ IDirect3DPixelShader9 *D3DContext::CreateFragmentProgram(DWORD **shaders, ShaderList *programs, jint flags) { DWORD *sourceCode; IDirect3DPixelShader9 *pProgram; J2dTraceLn1(J2D_TRACE_INFO, "D3DContext::CreateFragmentProgram: flags=%d", flags); sourceCode = shaders[flags]; if (FAILED(pd3dDevice->CreatePixelShader(sourceCode, &pProgram))) { J2dRlsTraceLn(J2D_TRACE_ERROR, "D3DContext::CreateFragmentProgram: error creating program"); return NULL; } // add it to the cache ShaderList_AddProgram(programs, ptr_to_jlong(pProgram), 0 /*unused*/, 0 /*unused*/, flags); return pProgram; } /** * Locates and enables a fragment program given a list of shader programs * (ShaderInfos), using this context's state and flags as search * parameters. The "flags" parameter is a bitwise-or'd value that helps * differentiate one program for another; the interpretation of this value * varies depending on the type of shader (BufImgOp, Paint, etc) but here * it is only used to find another ShaderInfo with that same "flags" value. */ HRESULT D3DContext::EnableFragmentProgram(DWORD **shaders, ShaderList *programList, jint flags) { HRESULT res; jlong programID; IDirect3DPixelShader9 *pProgram; J2dTraceLn(J2D_TRACE_INFO, "D3DContext::EnableFragmentProgram"); programID = ShaderList_FindProgram(programList, 0 /*unused*/, 0 /*unused*/, flags); pProgram = (IDirect3DPixelShader9 *)jlong_to_ptr(programID); if (pProgram == NULL) { pProgram = CreateFragmentProgram(shaders, programList, flags); if (pProgram == NULL) { return E_FAIL; } } if (FAILED(res = pd3dDevice->SetPixelShader(pProgram))) { J2dRlsTraceLn(J2D_TRACE_ERROR, "D3DContext::EnableFragmentProgram: error setting pixel shader"); return res; } return S_OK; } HRESULT D3DContext::EnableBasicGradientProgram(jint flags) { return EnableFragmentProgram((DWORD **)gradShaders, &basicGradPrograms, flags); } HRESULT D3DContext::EnableLinearGradientProgram(jint flags) { return EnableFragmentProgram((DWORD **)linearShaders, &linearGradPrograms, flags); } HRESULT D3DContext::EnableRadialGradientProgram(jint flags) { return EnableFragmentProgram((DWORD **)radialShaders, &radialGradPrograms, flags); } HRESULT D3DContext::EnableConvolveProgram(jint flags) { return EnableFragmentProgram((DWORD **)convolveShaders, &convolvePrograms, flags); } HRESULT D3DContext::EnableRescaleProgram(jint flags) { return EnableFragmentProgram((DWORD **)rescaleShaders, &rescalePrograms, flags); } HRESULT D3DContext::EnableLookupProgram(jint flags) { return EnableFragmentProgram((DWORD **)lookupShaders, &lookupPrograms, flags); } HRESULT D3DContext::EnableLCDTextProgram() { HRESULT res; J2dTraceLn(J2D_TRACE_INFO, "D3DContext::EnableLCDTextProgram"); if (lcdTextProgram == NULL) { if (FAILED(res = pd3dDevice->CreatePixelShader(lcdtext0, &lcdTextProgram))) { return res; } } if (FAILED(res = pd3dDevice->SetPixelShader(lcdTextProgram))) { J2dRlsTraceLn(J2D_TRACE_ERROR, "D3DContext::EnableLCDTextProgram: error setting pixel shader"); return res; } return S_OK; } HRESULT D3DContext::EnableAAParallelogramProgram() { HRESULT res; J2dTraceLn(J2D_TRACE_INFO, "D3DContext::EnableAAParallelogramProgram"); if (aaPgramProgram == NULL) { if (FAILED(res = pd3dDevice->CreatePixelShader(aapgram0, &aaPgramProgram))) { DebugPrintD3DError(res, "D3DContext::EnableAAParallelogramProgram: " "error creating pixel shader"); return res; } } if (FAILED(res = pd3dDevice->SetPixelShader(aaPgramProgram))) { DebugPrintD3DError(res, "D3DContext::EnableAAParallelogramProgram: " "error setting pixel shader"); return res; } return S_OK; } HRESULT D3DContext::DisableAAParallelogramProgram() { HRESULT res; J2dTraceLn(J2D_TRACE_INFO, "D3DContext::DisableAAParallelogramProgram"); if (aaPgramProgram != NULL) { if (FAILED(res = pd3dDevice->SetPixelShader(NULL))) { DebugPrintD3DError(res, "D3DContext::DisableAAParallelogramProgram: " "error clearing pixel shader"); return res; } } return S_OK; } BOOL D3DContext::IsAlphaRTSurfaceSupported() { HRESULT res = pd3dObject->CheckDeviceFormat(adapterOrdinal, devCaps.DeviceType, curParams.BackBufferFormat, D3DUSAGE_RENDERTARGET, D3DRTYPE_SURFACE, D3DFMT_A8R8G8B8); return SUCCEEDED(res); } BOOL D3DContext::IsAlphaRTTSupported() { HRESULT res = pd3dObject->CheckDeviceFormat(adapterOrdinal, devCaps.DeviceType, curParams.BackBufferFormat, D3DUSAGE_RENDERTARGET, D3DRTYPE_TEXTURE, D3DFMT_A8R8G8B8); return SUCCEEDED(res); } BOOL D3DContext::IsOpaqueRTTSupported() { HRESULT res = pd3dObject->CheckDeviceFormat(adapterOrdinal, devCaps.DeviceType, curParams.BackBufferFormat, D3DUSAGE_RENDERTARGET, D3DRTYPE_TEXTURE, curParams.BackBufferFormat); return SUCCEEDED(res); } HRESULT D3DContext::InitContextCaps() { J2dTraceLn(J2D_TRACE_INFO, "D3DContext::InitContextCaps"); J2dTraceLn1(J2D_TRACE_VERBOSE, " caps for adapter %d :", adapterOrdinal); if (pd3dDevice == NULL || pd3dObject == NULL) { contextCaps = CAPS_EMPTY; J2dRlsTraceLn(J2D_TRACE_VERBOSE, " | CAPS_EMPTY"); return E_FAIL; } contextCaps = CAPS_DEVICE_OK; J2dRlsTraceLn(J2D_TRACE_VERBOSE, " | CAPS_DEVICE_OK"); if (IsAlphaRTSurfaceSupported()) { contextCaps |= CAPS_RT_PLAIN_ALPHA; J2dRlsTraceLn(J2D_TRACE_VERBOSE, " | CAPS_RT_PLAIN_ALPHA"); } if (IsAlphaRTTSupported()) { contextCaps |= CAPS_RT_TEXTURE_ALPHA; J2dRlsTraceLn(J2D_TRACE_VERBOSE, " | CAPS_RT_TEXTURE_ALPHA"); } if (IsOpaqueRTTSupported()) { contextCaps |= CAPS_RT_TEXTURE_OPAQUE; J2dRlsTraceLn(J2D_TRACE_VERBOSE, " | CAPS_RT_TEXTURE_OPAQUE"); } if (IsPixelShader20Supported()) { contextCaps |= CAPS_LCD_SHADER | CAPS_BIOP_SHADER | CAPS_PS20; J2dRlsTraceLn(J2D_TRACE_VERBOSE, " | CAPS_LCD_SHADER | CAPS_BIOP_SHADER | CAPS_PS20"); // Pre-PS3.0 video boards are very slow with the AA shader, so // we will require PS30 hw even though the shader is compiled for 2.0a // if (IsGradientInstructionExtensionSupported()) { // contextCaps |= CAPS_AA_SHADER; // J2dRlsTraceLn(J2D_TRACE_VERBOSE, " | CAPS_AA_SHADER"); // } } if (IsPixelShader30Supported()) { if ((contextCaps & CAPS_AA_SHADER) == 0) { // This flag was not already mentioned above... J2dRlsTraceLn(J2D_TRACE_VERBOSE, " | CAPS_AA_SHADER"); } contextCaps |= CAPS_PS30 | CAPS_AA_SHADER; J2dRlsTraceLn(J2D_TRACE_VERBOSE, " | CAPS_PS30"); } if (IsMultiTexturingSupported()) { contextCaps |= CAPS_MULTITEXTURE; J2dRlsTraceLn(J2D_TRACE_VERBOSE, " | CAPS_MULTITEXTURE"); } if (!IsPow2TexturesOnly()) { contextCaps |= CAPS_TEXNONPOW2; J2dRlsTraceLn(J2D_TRACE_VERBOSE, " | CAPS_TEXNONPOW2"); } if (!IsSquareTexturesOnly()) { contextCaps |= CAPS_TEXNONSQUARE; J2dRlsTraceLn(J2D_TRACE_VERBOSE, " | CAPS_TEXNONSQUARE"); } return S_OK; }