void GSRendererHW::SetScaling() { GSVector2i crtc_size(GetDisplayRect().width(), GetDisplayRect().height()); // Framebuffer width is always a multiple of 64 so at certain cases it can't cover some weird width values. // 480P , 576P use width as 720 which is not referencable by FBW * 64. so it produces 704 ( the closest value multiple by 64). // In such cases, let's just use the CRTC width. int fb_width = max({ (int)m_context->FRAME.FBW * 64, crtc_size.x , 512 }); // GS doesn't have a specific register for the FrameBuffer height. so we get the height // from physical units of the display rectangle in case the game uses a heigher value of height. int fb_height = (fb_width < 1024) ? max(512, crtc_size.y) : 1024; int upscaled_fb_w = fb_width * m_upscale_multiplier; int upscaled_fb_h = fb_height * m_upscale_multiplier; bool good_rt_size = m_width >= upscaled_fb_w && m_height >= upscaled_fb_h; // No need to resize for native/custom resolutions as default size will be enough for native and we manually get RT Buffer size for custom. // don't resize until the display rectangle and register states are stabilized. if ( m_upscale_multiplier <= 1 || good_rt_size) return; m_tc->RemovePartial(); m_width = upscaled_fb_w; m_height = upscaled_fb_h; printf("Frame buffer size set to %dx%d (%dx%d)\n", fb_width, fb_height , m_width, m_height); }
GSVector2i GSRendererHW::GetInternalResolution() { GSVector2i dr(GetDisplayRect().width(), GetDisplayRect().height()); if (m_upscale_multiplier) return GSVector2i(dr.x * m_upscale_multiplier, dr.y * m_upscale_multiplier); else return GSVector2i(m_width, m_height); }
// InvalidateRange is an overrideable function that is used to invalidate the view // between two caret positions. This is used to invalidate bits of the window // when the selection is changed (using mouse selection or with SetSel()). // The default behaviour invalidates the lines (whole width of document) from // the top of start to the bottom of end (using the current character height). void CScrView::InvalidateRange(CPointAp start, CPointAp end, bool f /*=false*/) { if (start.y > scrollpos_.y + win_height_ || start.x > scrollpos_.x + win_width_ || end.y < scrollpos_.y || end.x < scrollpos_.x) { // All outside display area so do nothing. Note that this may appear to not be nec. // as Windows does this too but due to overflow problems is safer to do it here. return; } CSizeAp ss = caret_size(); // Also invalidate a row above and up to 3 rows below (this is necessary for stacked mode) start.y -= ss.cy; end.y += 3*ss.cy; if (start.x < scrollpos_.x) start.x = scrollpos_.x; if (start.y < scrollpos_.y) start.y = scrollpos_.y; if (end.y > scrollpos_.y + win_height_) end.y = scrollpos_.y + win_height_; // Invalidate the full width of display from top of start to bottom of end CRectAp rr(0, start.y, total_.cx, end.y); // Convert to device coords CRect norm_rect = ConvertToDP(rr); CRect cli; GetDisplayRect(&cli); // Invalidate the previous selection so that it is drawn unselected CRect rct; if (rct.IntersectRect(&cli, &norm_rect)) DoInvalidateRect(&norm_rect); }
// ディスプレイの番号を返す int Window::GetDisplayNumber( HWND window ) { int max, i, ret = 0, area[ 2 ] = { 0, 0 }; RECT win, disp; if ( window == NULL ) { return 0; } // ウィンドウの位置とサイズをRECTで取得 GetWindowRect( window, &win ); // ディスプレイの数 max = GetDisplayMax(); for ( i = 0; i < max; ++i ) { GetDisplayRect( i, &disp ); // そもそも当たってない。 if ( win.right < disp.left || disp.right < win.left || win.bottom < disp.top || disp.bottom < win.top ) { continue; } // ウィンドウはディスプレイの中 or 一部分が重なっている。 // ディスプレイのRECTをウィンドウと重なっている部分の大きさに置換。 if ( disp.left < win.left ) { disp.left = win.left; } if ( win.right < disp.right ) { disp.right = win.right; } if ( win.top < disp.top ) { disp.top = win.top; } if ( win.bottom < disp.bottom ) { disp.bottom = win.bottom; } // RECTの面積を計算。 area[ 1 ] = ( disp.right - disp.left ) * ( disp.bottom - disp.top ); if ( area[ 0 ] < area[ 1 ] ) { area[ 0 ] = area[ 1 ]; ret = i; } } return ret; }
void wxJigsawEditorCanvas::OnLeftDown( wxMouseEvent& event ) { SetFocus(); wxRect displayRect = GetDisplayRect(GetScrollPosition()); m_MouseDownPos = event.GetPosition(); do { if(!m_View) break; wxPoint diagramPoint = PointToViewPoint(m_MouseDownPos); wxJigsawShape * shape(NULL); wxJigsawEditorCanvasHitTest hitTest = HitTest(m_MouseDownPos, infoSelectedShape, NULL); static wxJigsawShapeList m_PaletteShapeList; static wxJigsawShapeList *pPaletteShapeList = NULL; if(infoSelectedShape.GetResult() == wxJigsawShape::wxJigsawShapeHitTest::wxJS_HITTEST_SLOT) { //Must save the current selected category or save the current search if(!pPaletteShapeList) pPaletteShapeList = wxJigsawEditorMainFrame::Get()->GetPalette()->GetShapes(); wxJigsawEditorMainFrame::Get()->SearchStyle(infoSelectedShape.GetInputParameterStyle()); } else if(pPaletteShapeList) { //Restore the search or selected category if(wxJigsawEditorMainFrame::Get()->IsSearchStyleSelected()) { wxJigsawEditorMainFrame::Get()->GetPalette()->SetShapes(pPaletteShapeList); wxJigsawEditorMainFrame::Get()->GetPalette()->Refresh(); wxJigsawEditorMainFrame::Get()->GetPalette()->AdjustScrollBars(); } pPaletteShapeList = NULL; } switch(hitTest) { case wxJSEC_HITTEST_SHAPE: shape = m_View->GetShapeFromPoint(m_DoubleBufferDC, diagramPoint, infoSelectedShape, NULL); if(shape) { SetSelectedShape(shape); CaptureMouse(); } break; case wxJSEC_HITTEST_NONE: SetSelectedObject(NULL); default: break; } } while(false); RefreshBuffer(); }
int Window::GetDisplayRect( long displaynum, RECT *rect ) { int ret, x, y, w, h; if ( rect == NULL ) { return 3; } if ( ( ret = GetDisplayRect( displaynum, &x, &y, &w, &h ) ) ) { return ret; } rect->left = x; rect->top = y; rect->right = x + w; rect->bottom = y + h; return 0; }
void GSRendererSW::VSync(int field) { Sync(0); // IncAge might delete a cached texture in use if(0) if(LOG) { fprintf(s_fp, "%llu\n", m_perfmon.GetFrame()); GSVector4i dr = GetDisplayRect(); GSVector4i fr = GetFrameRect(); fprintf(s_fp, "dr %d %d %d %d, fr %d %d %d %d\n", dr.x, dr.y, dr.z, dr.w, fr.x, fr.y, fr.z, fr.w); m_regs->Dump(s_fp); fflush(s_fp); } /* int draw[8], sum = 0; for(size_t i = 0; i < countof(draw); i++) { draw[i] = m_perfmon.CPU(GSPerfMon::WorkerDraw0 + i); sum += draw[i]; } printf("CPU %d Sync %d W %d %d %d %d %d %d %d %d (%d)\n", m_perfmon.CPU(GSPerfMon::Main), m_perfmon.CPU(GSPerfMon::Sync), draw[0], draw[1], draw[2], draw[3], draw[4], draw[5], draw[6], draw[7], sum); // */ GSRenderer::VSync(field); m_tc->IncAge(); // if((m_perfmon.GetFrame() & 255) == 0) m_rl->PrintStats(); }
SIZE CImage::GetDefaultScaleSize() { RECT ParentRect = GetDisplayRect(); //{0, 0, 1280, 960}; //const вызывает инициализацию переменных ДО входа в функцию (по возможности). что нам не подходит - хочется видеть последовательность действий! int lScreenWidth = ParentRect.right - ParentRect.left; int lScreenHeight = ParentRect.bottom - ParentRect.top; RECT ImgRect = {0,0,lScreenWidth,lScreenHeight}; // Тут используем НЕ this->lWidth & this->lHeight // а InfoPage.lWidth & InfoPage.lHeight. Т.к. именно они // будут содержать актуальную информацию о декодируемой СТРАНИЦЕ многостраничного документа u32 Zoom; if (g_Plugin.ZoomAuto || g_Plugin.FlagsWork & FW_QUICK_VIEW) { //const вызывает инициализацию переменных ДО входа в функцию (по возможности). что нам не подходит - хочется видеть последовательность действий! u32 ZoomW = MulDivU32(lScreenWidth, 0x10000, Info.lWidth /*dds->m_lWorkWidth*/); u32 ZoomH = MulDivU32(lScreenHeight, 0x10000, Info.lHeight /*dds->m_lWorkHeight*/); Zoom = (g_Plugin.ZoomAuto == ZA_FIT || g_Plugin.FlagsWork & FW_QUICK_VIEW) ? Min(ZoomW, ZoomH) : Max(ZoomW, ZoomH); if (!Zoom) Zoom = 1; if (g_Plugin.bAutoZoomMin && Zoom < g_Plugin.AutoZoomMin) Zoom = g_Plugin.AutoZoomMin; if (g_Plugin.bAutoZoomMax && Zoom > g_Plugin.AutoZoomMax) Zoom = g_Plugin.AutoZoomMax; } else { Zoom = g_Plugin.AbsoluteZoom ? g_Plugin.AbsoluteZoom : g_Plugin.Zoom; } //const вызывает инициализацию переменных ДО входа в функцию (по возможности). что нам не подходит - хочется видеть последовательность действий! int lWidth = MulDivU32R(Info.lWidth, Zoom, 0x10000); int lHeight = MulDivU32R(Info.lHeight, Zoom, 0x10000); SIZE sz = {lWidth,lHeight}; return sz; }
// Is the caret currently within the display window? BOOL CScrView::CaretDisplayed(CSizeAp win_size) { if (!caret_mode_ || ! caret_changes_) return FALSE; // Work out bounding rectangles of display and caret CRectAp disp_rect; // Display rect within doc if (win_size != null_size) { // Use passed size disp_rect = CRectAp(scrollpos_, win_size); } else { CRect rct; GetDisplayRect(&rct); disp_rect = ConvertFromDP(rct); } CRectAp caret_rect(caretpos_, caret_size()); CRectAp intersect_rect; // Intersection of display & caret return intersect_rect.IntersectRect(disp_rect, caret_rect); }
bool GSRenderer::Merge(int field) { bool en[2]; GSVector4i fr[2]; GSVector4i dr[2]; GSVector2i display_baseline = { INT_MAX, INT_MAX }; GSVector2i frame_baseline = { INT_MAX, INT_MAX }; for(int i = 0; i < 2; i++) { en[i] = IsEnabled(i); if(en[i]) { fr[i] = GetFrameRect(i); dr[i] = GetDisplayRect(i); display_baseline.x = min(dr[i].left, display_baseline.x); display_baseline.y = min(dr[i].top, display_baseline.y); frame_baseline.x = min(fr[i].left, frame_baseline.x); frame_baseline.y = min(fr[i].top, frame_baseline.y); //printf("[%d]: %d %d %d %d, %d %d %d %d\n", i, fr[i].x,fr[i].y,fr[i].z,fr[i].w , dr[i].x,dr[i].y,dr[i].z,dr[i].w); } } if(!en[0] && !en[1]) { return false; } GL_PUSH("Renderer Merge %d (0: enabled %d 0x%x, 1: enabled %d 0x%x)", s_n, en[0], m_regs->DISP[0].DISPFB.Block(), en[1], m_regs->DISP[1].DISPFB.Block()); // try to avoid fullscreen blur, could be nice on tv but on a monitor it's like double vision, hurts my eyes (persona 4, guitar hero) // // NOTE: probably the technique explained in graphtip.pdf (Antialiasing by Supersampling / 4. Reading Odd/Even Scan Lines Separately with the PCRTC then Blending) bool samesrc = en[0] && en[1] && m_regs->DISP[0].DISPFB.FBP == m_regs->DISP[1].DISPFB.FBP && m_regs->DISP[0].DISPFB.FBW == m_regs->DISP[1].DISPFB.FBW && m_regs->DISP[0].DISPFB.PSM == m_regs->DISP[1].DISPFB.PSM; if(samesrc /*&& m_regs->PMODE.SLBG == 0 && m_regs->PMODE.MMOD == 1 && m_regs->PMODE.ALP == 0x80*/) { // persona 4: // // fr[0] = 0 0 640 448 // fr[1] = 0 1 640 448 // dr[0] = 159 50 779 498 // dr[1] = 159 50 779 497 // // second image shifted up by 1 pixel and blended over itself // // god of war: // // fr[0] = 0 1 512 448 // fr[1] = 0 0 512 448 // dr[0] = 127 50 639 497 // dr[1] = 127 50 639 498 // // same just the first image shifted // // These kinds of cases are now fixed by the more generic frame_diff code below, as the code here was too specific and has become obsolete. // NOTE: Persona 4 and God Of War are not rare exceptions, many games have the same(or very similar) offsets. int topDiff = fr[0].top - fr[1].top; if (dr[0].eq(dr[1]) && (fr[0].eq(fr[1] + GSVector4i(0, topDiff, 0, topDiff)) || fr[1].eq(fr[0] + GSVector4i(0, topDiff, 0, topDiff)))) { // dq5: // // fr[0] = 0 1 512 445 // fr[1] = 0 0 512 444 // dr[0] = 127 50 639 494 // dr[1] = 127 50 639 494 int top = min(fr[0].top, fr[1].top); int bottom = min(fr[0].bottom, fr[1].bottom); fr[0].top = fr[1].top = top; fr[0].bottom = fr[1].bottom = bottom; } } GSVector2i fs(0, 0); GSVector2i ds(0, 0); GSTexture* tex[3] = {NULL, NULL, NULL}; int y_offset[3] = {0, 0, 0}; s_n++; bool feedback_merge = m_regs->EXTWRITE.WRITE == 1; if(samesrc && fr[0].bottom == fr[1].bottom && !feedback_merge) { tex[0] = GetOutput(0, y_offset[0]); tex[1] = tex[0]; // saves one texture fetch y_offset[1] = y_offset[0]; } else { if(en[0]) tex[0] = GetOutput(0, y_offset[0]); if(en[1]) tex[1] = GetOutput(1, y_offset[1]); if(feedback_merge) tex[2] = GetFeedbackOutput(); } GSVector4 src[2]; GSVector4 src_hw[2]; GSVector4 dst[2]; for(int i = 0; i < 2; i++) { if(!en[i] || !tex[i]) continue; GSVector4i r = fr[i]; GSVector4 scale = GSVector4(tex[i]->GetScale()).xyxy(); src[i] = GSVector4(r) * scale / GSVector4(tex[i]->GetSize()).xyxy(); src_hw[i] = (GSVector4(r) + GSVector4 (0, y_offset[i], 0, y_offset[i])) * scale / GSVector4(tex[i]->GetSize()).xyxy(); GSVector2 off(0); GSVector2i display_diff(dr[i].left - display_baseline.x, dr[i].top - display_baseline.y); GSVector2i frame_diff(fr[i].left - frame_baseline.x, fr[i].top - frame_baseline.y); // Time Crisis 2/3 uses two side by side images when in split screen mode. // Though ignore cases where baseline and display rectangle offsets only differ by 1 pixel, causes blurring and wrong resolution output on FFXII if(display_diff.x > 2) { off.x = tex[i]->GetScale().x * display_diff.x; } // If the DX offset is too small then consider the status of frame memory offsets, prevents blurring on Tenchu: Fatal Shadows, Worms 3D else if(display_diff.x != frame_diff.x) { off.x = tex[i]->GetScale().x * frame_diff.x; } if(display_diff.y >= 4) // Shouldn't this be >= 2? { off.y = tex[i]->GetScale().y * display_diff.y; if(m_regs->SMODE2.INT && m_regs->SMODE2.FFMD) { off.y /= 2; } } else if(display_diff.y != frame_diff.y) { off.y = tex[i]->GetScale().y * frame_diff.y; } dst[i] = GSVector4(off).xyxy() + scale * GSVector4(r.rsize()); fs.x = max(fs.x, (int)(dst[i].z + 0.5f)); fs.y = max(fs.y, (int)(dst[i].w + 0.5f)); } ds = fs; if(m_regs->SMODE2.INT && m_regs->SMODE2.FFMD) { ds.y *= 2; } m_real_size = ds; bool slbg = m_regs->PMODE.SLBG; if(tex[0] || tex[1]) { if(tex[0] == tex[1] && !slbg && (src[0] == src[1] & dst[0] == dst[1]).alltrue()) { // the two outputs are identical, skip drawing one of them (the one that is alpha blended) tex[0] = NULL; } GSVector4 c = GSVector4((int)m_regs->BGCOLOR.R, (int)m_regs->BGCOLOR.G, (int)m_regs->BGCOLOR.B, (int)m_regs->PMODE.ALP) / 255; m_dev->Merge(tex, src_hw, dst, fs, m_regs->PMODE, m_regs->EXTBUF, c); if(m_regs->SMODE2.INT && m_interlace > 0) { if(m_interlace == 7 && m_regs->SMODE2.FFMD) // Auto interlace enabled / Odd frame interlace setting { int field2 = 0; int mode = 2; m_dev->Interlace(ds, field ^ field2, mode, tex[1] ? tex[1]->GetScale().y : tex[0]->GetScale().y); } else { int field2 = 1 - ((m_interlace - 1) & 1); int mode = (m_interlace - 1) >> 1; m_dev->Interlace(ds, field ^ field2, mode, tex[1] ? tex[1]->GetScale().y : tex[0]->GetScale().y); } } if(m_shadeboost) { m_dev->ShadeBoost(); } if(m_shaderfx) { m_dev->ExternalFX(); } if(m_fxaa) { m_dev->FXAA(); } } return true; }
// ディスプレイのサイズを返す int Window::GetDisplaySize( long displaynum, int *width, int *height ) { return GetDisplayRect( displaynum, NULL, NULL, width, height ); }
// update_bars() is called if anything changes that will cause the scroll // bars to change (move/resize etc). This will happen if total_, page_ // or line_ sizes change, the window is resized, font changed, etc. // If the parameter 'newpos' is not null_point then the display position // will be moved to that position. Note that the display may be redrawn. void CScrView::update_bars(CPointAp newpos) { if (in_update_ || m_hWnd == NULL) return; in_update_ = TRUE; // TRACE0("--- update_bars\n"); check_coords(); // Check that mapping mode not changed caret_hide(); // Get info needed in later calcs TEXTMETRIC tm; { CClientDC dc(this); OnPrepareDC(&dc); dc.GetTextMetrics(&tm); } // Get display area and convert to logical units (but with +ve values) CRect cli; GetDisplayRect(&cli); //CRectAp doc_rect = ConvertFromDP(cli) - GetScroll(); // If newpos not specified use old pos if (newpos == null_point) newpos = scrollpos_; ASSERT(newpos.x >= 0 && newpos.y >= 0); if (!page_specified_ || !line_specified_) { // Calculate amount of line and page scroll based on size of font if (!page_specified_) { // Recalc page size int lines = cli.Height()/tm.tmHeight + tm.tmExternalLeading - 1; if (lines < 1) lines = 1; page_.cx = cli.Width() - tm.tmAveCharWidth; page_.cy = lines * (tm.tmHeight + tm.tmExternalLeading); } if (!line_specified_) { // Recalc line size line_.cx = tm.tmAveCharWidth; line_.cy = tm.tmHeight + tm.tmExternalLeading; } } // Update display and scroll bars. ASSERT(total_.cx >= 0 && total_.cy >= 0); // If display position has changed redraw/scroll to it if (newpos != scrollpos_) { scroll_up_ = newpos.y < scrollpos_.y; // Get scroll & new positions in real logical coords CPointAp t_scroll = scrollpos_; if (negx()) t_scroll.x = -t_scroll.x; if (negy()) t_scroll.y = -t_scroll.y; CPointAp t_newpos = newpos; if (negx()) t_newpos.x = -t_newpos.x; if (negy()) t_newpos.y = -t_newpos.y; // // For OWNDC window and mapping mode != MM_TEXT ScrollWindow // // does not move caret properly!?!? - so we do it ourselves! __int64 tmp = t_scroll.y < t_newpos.y ? t_newpos.y - t_scroll.y : t_scroll.y - t_newpos.y; if (abs(int(t_scroll.x - t_newpos.x)) > cli.Width() || tmp > __int64(cli.Height())) DoInvalidate(); // ScrollWindow() can't handle big numbers else { // Work out scroll amount in device coordinates // LPtoDP can't handle more than 16 bits (signed) CPoint dev_scroll, dev_newpos; dev_scroll.x = t_scroll.x; dev_scroll.y = int(t_scroll.y); dev_newpos.x = t_newpos.x; dev_newpos.y = int(t_newpos.y); { // Put in compound statement so DC released ASAP CClientDC dc(this); OnPrepareDC(&dc); dc.LPtoDP(&dev_scroll); dc.LPtoDP(&dev_newpos); } DoScrollWindow(dev_scroll.x - dev_newpos.x, dev_scroll.y - dev_newpos.y); } } if (total_.cx <= 0) DoHScroll(0, 0, 0); // disable scroll bar else if (newpos.x > total_.cx - cli.Width()) { // Handle scroll position past right edge DoHScroll(int(((__int64)(newpos.x + cli.Width()) * maxbar_.cx) / total_.cx), (cli.Width() * maxbar_.cx) / total_.cx + 1, int(((__int64)newpos.x * maxbar_.cx) / total_.cx) ); } else { DoHScroll(maxbar_.cx, (cli.Width() * maxbar_.cx) / total_.cx + 1, int(((__int64)newpos.x * maxbar_.cx) / total_.cx) ); } if (total_.cy <= 0) DoVScroll(0, 0, 0); // disable scroll bar else if (newpos.y > total_.cy - cli.Height()) { // Handle scroll position if (somehow) moved past end DoVScroll(int(((__int64)(newpos.y + cli.Height()) * maxbar_.cy) / total_.cy), int((cli.Height() * maxbar_.cy) / total_.cy + 1), int(((__int64)newpos.y * maxbar_.cy) / total_.cy) ); } else { DoVScroll(int(maxbar_.cy), int((cli.Height() * maxbar_.cy) / total_.cy + 1), int(((__int64)newpos.y * maxbar_.cy) / total_.cy) ); } // Store new position and force redraw before restore of scroll_up_ scrollpos_ = newpos; // Keep track of where we moved to ASSERT(scrollpos_.x >= 0 && scrollpos_.y >= 0); // if (GetFocus() == this) caret_show(); DoUpdateWindow(); // Force redraw before changing scroll_up_ scroll_up_ = FALSE; // Make sure redraw defaults to downwards // Get client window again as changing the scroll bars may have changed it // (and nested size event was blocked by in_update_ flag) GetDisplayRect(&cli); CRectAp doc_rect = ConvertFromDP(cli); win_height_ = doc_rect.bottom - doc_rect.top; win_width_ = doc_rect.right - doc_rect.left; AfterScroll(newpos); in_update_ = FALSE; }
bool wxJigsawEditorCanvas::AutoScroll(const wxPoint & currentPos, const wxPoint & scrollPos) { bool res = false; // If mouse is captured set rect coords if inside the image and autoscroll if posible // get image display rectangle wxRect displayRect = GetDisplayRect(scrollPos); wxSize oldSelection(m_TR.x-m_BL.x, m_TR.y-m_BL.y); // check if the current drag position is inside the image - do not allow to draw rectangle out of the image m_TR.x = wxMin(wxMax(currentPos.x, displayRect.GetLeft()), displayRect.GetRight()); m_TR.y = wxMin(wxMax(currentPos.y, displayRect.GetTop()), displayRect.GetBottom()); // Check current drag position and update scroll regularly if(currentPos.x <= 0) { Scroll(wxPoint(wxMax(scrollPos.x - wxJigsawEditorCanvas::ScrollIncrement, 0), -1)); m_BL.x += (scrollPos.x - GetScrollPosition().x); res = true; } if(currentPos.y <= 0) { Scroll(wxPoint(-1,wxMax(scrollPos.y - wxJigsawEditorCanvas::ScrollIncrement, 0))); m_BL.y += (scrollPos.y - GetScrollPosition().y); res = true; } if(currentPos.x >= GetClientSize().GetWidth()) { Scroll(wxPoint(scrollPos.x + wxJigsawEditorCanvas::ScrollIncrement, -1)); m_BL.x -= (GetScrollPosition().x - scrollPos.x); res = true; } if(currentPos.y >= GetClientSize().y) { Scroll(wxPoint(-1, scrollPos.y + wxJigsawEditorCanvas::ScrollIncrement)); m_BL.y -= (GetScrollPosition().y - scrollPos.y); res = true; } m_SelectionRect = wxRect(wxMin(m_TR.x, m_BL.x), wxMin(m_TR.y, m_BL.y), abs(m_TR.x-m_BL.x), abs(m_TR.y-m_BL.y)); if(HasCapture()) { if(m_View && m_View->GetSelectedObject() && (m_Mode == wxJSEC_MODE_DRAGGING)) { /*wxSize offset(m_TR.x-m_BL.x-oldSelection.GetWidth(), m_TR.y-m_BL.y-oldSelection.GetHeight());*/ wxPoint cursorPosOnDiagram = currentPos; cursorPosOnDiagram.x /= m_View->GetScale(); cursorPosOnDiagram.y /= m_View->GetScale(); cursorPosOnDiagram.x -= m_View->GetViewOffset().x; cursorPosOnDiagram.y -= m_View->GetViewOffset().y; m_SelectedObjectOffset.x /= m_View->GetScale(); m_SelectedObjectOffset.y /= m_View->GetScale(); m_View->GetSelectedObject()->Drag(m_DoubleBufferDC, cursorPosOnDiagram, m_SelectedObjectOffset, m_View->GetScale()); GetDocument()->Modify(true); AdjustScrollBars(); } } return res; }
void CScrView::OnSelUpdate(CPoint point) { // Keep the last drag position so we only invalidate what has changed. CPointAp last; ASSERT(basepos_ == caretpos_ || basepos_ == selpos_); if (caretpos_ == basepos_) last = selpos_; else last = caretpos_; CRect cli; // Display area (client coords) GetDisplayRect(&cli); CRectAp disp = ConvertFromDP(cli); // Display area ("cli") in our coords CPointAp pp = ConvertFromDP(point); // Mouse point ("point") in our coords // Get display rect shrunk to only contain whole rows/columns CRectAp rr(disp); rr.top = ((rr.top - 1)/line_.cy + 1)*line_.cy; rr.left = ((rr.left- 1)/line_.cx + 1)*line_.cx; rr.bottom = (rr.bottom/line_.cy)*line_.cy; rr.right = (rr.right /line_.cx)*line_.cx; // Test if we are about to scroll or very close to edge if (!rr.PtInRect(pp)) { // TODO: We probably should handle vertical and horizontal auto-scrolling // separately and not allow both at the same time, to avoid confusion. CPointAp newpos(-1, -1); // New scroll position (our coords) static clock_t last_clock = 0; if (pp.x < rr.left) { newpos.x = scrollpos_.x - line_.cx; } else if (pp.x >= rr.right) { newpos.x = scrollpos_.x + line_.cx; } if (pp.y < rr.top) { // Use clock to ensure scrolling does not get faster with more mouse move events clock_t curr_clock = clock(); clock_t diff_clock = curr_clock - last_clock; if (diff_clock > CLOCKS_PER_SEC/5) diff_clock = CLOCKS_PER_SEC/5; // Scroll speed depends on distance mouse is above window // int diff_pixel = ((disp.top - pp.y)*(disp.top - pp.y))/10; int diff_pixel = int(disp.top - pp.y); int autoscroll = (int)pow((double)diff_pixel, autoscroll_accel_/10.0); __int64 to_move = 0; // How much to scroll up // Always scroll so that top of text line is at top of window if (disp.top < rr.top) to_move = line_.cy - (rr.top - disp.top); // If enough time has passed or mouse is far enough above the window... if (disp.top > pp.y && diff_clock * autoscroll >= CLOCKS_PER_SEC/5) { // Scroll according to time and distance to_move += line_.cy*((diff_clock * autoscroll)/(CLOCKS_PER_SEC/5)); last_clock = curr_clock; } newpos.y = scrollpos_.y - to_move; if (newpos.y < 0) newpos.y = 0; } else if (pp.y >= rr.bottom) { // See above comments (same but for bottom of window not top) clock_t curr_clock = clock(); clock_t diff_clock = curr_clock - last_clock; if (diff_clock > CLOCKS_PER_SEC/5) diff_clock = CLOCKS_PER_SEC/5; int diff_pixel = int(pp.y - disp.bottom); int autoscroll = (int)pow((double)diff_pixel, autoscroll_accel_/10.0); __int64 to_move = 0; if (disp.bottom > rr.bottom) to_move = line_.cy - (disp.bottom - rr.bottom); if (disp.bottom < pp.y && diff_clock * autoscroll >= CLOCKS_PER_SEC/5) { to_move += line_.cy*((diff_clock * autoscroll)/(CLOCKS_PER_SEC/5)); last_clock = curr_clock; } newpos.y = scrollpos_.y + to_move; if (newpos.y < 0) newpos.y = 0; } SetScroll(newpos, TRUE); } // Set end of selection based on this point CPoint ptmp = point; // Get pp based on new value of "point" // Don't allow selection to extend outside window if (ptmp.y < cli.top) ptmp.y = cli.top; else if (ptmp.y >= cli.bottom) ptmp.y = cli.bottom - 1; if (ptmp.x < cli.left) ptmp.x = cli.left; else if (ptmp.x >= cli.right) ptmp.x = cli.right - 1; // Work out new selection pp = ConvertFromDP(ptmp); ValidateCaret(pp, FALSE); if (pp.y < basepos_.y || (pp.y == basepos_.y && pp.x < basepos_.x)) { caretpos_ = pp; selpos_ = basepos_; } else { caretpos_ = basepos_; selpos_ = pp; } // Allow derived class to invalidate the whole selection if (pp.y != last.y || pp.x != last.x) InvalidateRange(caretpos_, selpos_, true); // Invalidate everything that's changed if (pp.y < last.y || (pp.y == last.y && pp.x < last.x)) InvalidateRange(pp, last); else if (pp.y != last.y || pp.x != last.x) InvalidateRange(last, pp); }
LRESULT KCanvasWindow::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_CREATE: m_hWnd = hWnd; break; case WM_PAINT: if (hMetaFile || hNTMetaFile) { PAINTSTRUCT ps; RECT rect; HBRUSH bbrush; BeginPaint(hWnd, &ps); GetDisplayRect(&rect); bbrush = CreateSolidBrush(canvas_backcolor); FillRect(ps.hdc, &rect, bbrush); DeleteObject(bbrush); if (hMetaFile) if (canvas_delay==0) PlayEnhMetaFile(ps.hdc, hMetaFile, &rect); else { KProgress Progress; Progress.Create(EmfScope.hinst_EmfScope, EmfScope.m_hWnd, IDD_PROGRESS, IDC_PROGRESS, IDC_NUMBER, canvas_delay); EnumEnhMetaFile(ps.hdc, hMetaFile, (ENHMFENUMPROC) SlowPaintEMF, & Progress, &rect); Progress.Destroy(); SetFocus(EmfScope.m_hWnd); } else if (hNTMetaFile) PlayNTEnhMetaFile(ps.hdc, hNTMetaFile, &rect); EndPaint(hWnd, &ps); } else return DefWindowProc(hWnd, uMsg, wParam, lParam); break; case WM_DESTROY: if (hMetaFile) { DeleteEnhMetaFile(hMetaFile); hMetaFile = NULL; } if (hNTMetaFile) { delete hNTMetaFile; hNTMetaFile = NULL; } break; case WM_HSCROLL: case WM_VSCROLL: Scroll (uMsg, HIWORD (wParam), LOWORD(wParam)); break; case WM_SIZE: SetScaleColor(0, 0); break; default: return DefWindowProc(hWnd, uMsg, wParam, lParam); } return 0; }
/*! @brief ウィンドウの設定を変更する @par 関数説明 ウィンドウスタイルを設定する。 @param Type WT_で始まる定数。 @return false=設定反映かSetWindowPosでの設定失敗。 @return true=SetWindowPosでの設定成功。 */ BOOL Window::SetWindow( int type, int display ) { HWND myhwnd = NULL;//ウィンドウハンドル RECT myrect;//RECT構造体 BOOL ret; // ディスプレイ番号から領域を割り出して移動してからサイズ変更とかを行うように書き換え switch ( type ) { case WT_DEFAULT://サイズ固定のウィンドウ //ウィンドウスタイルの設定 SetWindowStyle( WT_DEFAULT ); //ウィンドウスタイルの変更 SetWindowLong( GetWindowHandle(), GWL_STYLE, GetWindowStyle() ); wd.flags = SWP_SHOWWINDOW; wd.winpos = HWND_TOP; //ウィンドウのサイズや位置を変更 ret = SetWindowPos( GetWindowHandle(), wd.winpos, GetPositionX(), GetPositionY(),//CW_USEDEFAULT, CW_USEDEFAULT, GetWindowWidth(), GetWindowHeight(), wd.flags ); SetPositionX( GetPositionX( 1 ) ); SetPositionY( GetPositionY( 1 ) ); return ret; case WT_NORMAL://通常のリサイズ可能ウィンドウ //ウィンドウスタイルの設定 SetWindowStyle( WT_NORMAL ); //ウィンドウスタイルの変更 SetWindowLong( GetWindowHandle(), GWL_STYLE, GetWindowStyle() ); wd.flags = SWP_SHOWWINDOW; //ウィンドウのサイズや位置を変更 wd.winpos = HWND_TOP; ret = SetWindowPos( GetWindowHandle(), wd.winpos, GetPositionX(), GetPositionY(),//CW_USEDEFAULT, CW_USEDEFAULT, GetWindowWidth(), GetWindowHeight(), wd.flags ); SetPositionX( GetPositionX( 1 ) ); SetPositionY( GetPositionY( 1 ) ); return ret; case WT_NOFRAME: case WT_FULLSCREEN: //ウィンドウスタイルの設定 SetWindowStyle( WT_NOFRAME ); //ウィンドウスタイルの変更 SetWindowLong( GetWindowHandle(), GWL_STYLE, GetWindowStyle() ); wd.flags = SWP_SHOWWINDOW; wd.winpos = HWND_TOP; //ウィンドウのサイズや位置を変更 return SetWindowPos( GetWindowHandle(), wd.winpos, GetPositionX(), GetPositionY(), GetWindowWidth(), GetWindowHeight(), wd.flags ); case WT_NORESIZEFULLSCREEN://画面設定を変えないフルスクリーン //プログラムを起動したデスクトップサイズのウィンドウで //描写優先度が一番上のウィンドウを生成する //ウィンドウスタイルの設定 SetWindowStyle( WT_NORESIZEFULLSCREEN ); // 対象ディスプレイのサイズなどを取得。 if ( GetDisplayRect( display, &myrect ) == 0 ) { // 成功したので、うまい具合に設定。 // 位置はディスプレイの左上。 SetPositionXY( myrect.left, myrect.top ); // サイズはディスプレイに合わせる。 SetWindowSize( myrect.right - myrect.left, myrect.bottom - myrect.top ); } else { // 失敗したので、デスクトップから取得。 //デスクトップのウィンドウハンドル取得 myhwnd = GetDesktopWindow(); //デスクトップのサイズなどを取得 if ( myhwnd && GetClientRect( myhwnd, &myrect ) ) { //位置は原点に設定 SetPositionXY( 0, 0 ); //サイズはデスクトップと同じサイズに設定 SetWindowSize( myrect.right, myrect.bottom ); } } //ウィンドウスタイルの変更 SetWindowLong( GetWindowHandle(), GWL_STYLE, GetWindowStyle() ); wd.flags = SWP_SHOWWINDOW; wd.winpos = HWND_TOPMOST; //ウィンドウのサイズや位置を変更 return SetWindowPos( GetWindowHandle(), wd.winpos, GetPositionX(), GetPositionY(), GetWindowWidth(), GetWindowHeight(), wd.flags ); default: //後で考える //リサイズ程度 MoveWindow( GetWindowHandle(), GetPositionX(), GetPositionY(), GetWindowWidth(), GetWindowHeight(), TRUE ); break; } return 0; }
void GSRenderer::VSync(int field) { GSPerfMonAutoTimer pmat(&m_perfmon); m_perfmon.Put(GSPerfMon::Frame); Flush(); if(!m_dev->IsLost(true)) { if(!Merge(field ? 1 : 0)) { return; } } else { ResetDevice(); } m_dev->AgePool(); // osd if((m_perfmon.GetFrame() & 0x1f) == 0) { m_perfmon.Update(); double fps = 1000.0f / m_perfmon.Get(GSPerfMon::Frame); GSVector4i r = GetDisplayRect(); string s; #ifdef GSTITLEINFO_API_FORCE_VERBOSE if (1)//force verbose reply #else if (m_wnd->IsManaged()) #endif { //GSdx owns the window's title, be verbose. string s2 = m_regs->SMODE2.INT ? (string("Interlaced ") + (m_regs->SMODE2.FFMD ? "(frame)" : "(field)")) : "Progressive"; s = format( "%lld | %d x %d | %.2f fps (%d%%) | %s - %s | %s | %d/%d/%d | %d%% CPU | %.2f | %.2f", m_perfmon.GetFrame(), r.width(), r.height(), fps, (int)(100.0 * fps / GetFPS()), s2.c_str(), theApp.m_gs_interlace[m_interlace].name.c_str(), theApp.m_gs_aspectratio[m_aspectratio].name.c_str(), (int)m_perfmon.Get(GSPerfMon::SyncPoint), (int)m_perfmon.Get(GSPerfMon::Prim), (int)m_perfmon.Get(GSPerfMon::Draw), m_perfmon.CPU(), m_perfmon.Get(GSPerfMon::Swizzle) / 1024, m_perfmon.Get(GSPerfMon::Unswizzle) / 1024 ); double fillrate = m_perfmon.Get(GSPerfMon::Fillrate); if(fillrate > 0) { s += format(" | %.2f mpps", fps * fillrate / (1024 * 1024)); int sum = 0; for(int i = 0; i < 16; i++) { sum += m_perfmon.CPU(GSPerfMon::WorkerDraw0 + i); } s += format(" | %d%% CPU", sum); } } else { // Satisfy PCSX2's request for title info: minimal verbosity due to more external title text s = format("%dx%d | %s", r.width(), r.height(), theApp.m_gs_interlace[m_interlace].name.c_str()); } if(m_capture.IsCapturing()) { s += " | Recording..."; } if(m_wnd->IsManaged()) { m_wnd->SetWindowText(s.c_str()); } else { // note: do not use TryEnterCriticalSection. It is unnecessary code complication in // an area that absolutely does not matter (even if it were 100 times slower, it wouldn't // be noticeable). Besides, these locks are extremely short -- overhead of conditional // is way more expensive than just waiting for the CriticalSection in 1 of 10,000,000 tries. --air GSAutoLock lock(&m_pGSsetTitle_Crit); strncpy(m_GStitleInfoBuffer, s.c_str(), countof(m_GStitleInfoBuffer) - 1); m_GStitleInfoBuffer[sizeof(m_GStitleInfoBuffer) - 1] = 0; // make sure null terminated even if text overflows } } else { // [TODO] // We don't have window title rights, or the window has no title, // so let's use actual OSD! } if(m_frameskip) { return; } // present m_dev->Present(m_wnd->GetClientRect().fit(m_aspectratio), m_shader); // snapshot if(!m_snapshot.empty()) { bool shift = false; #ifdef _WINDOWS shift = !!(::GetAsyncKeyState(VK_SHIFT) & 0x8000); #else shift = m_shift_key; #endif if(!m_dump && shift) { GSFreezeData fd; fd.size = 0; fd.data = NULL; Freeze(&fd, true); fd.data = new uint8[fd.size]; Freeze(&fd, false); m_dump.Open(m_snapshot, m_crc, fd, m_regs); delete [] fd.data; } if(GSTexture* t = m_dev->GetCurrent()) { t->Save(m_snapshot + ".bmp"); } m_snapshot.clear(); } else { if(m_dump) { bool control = false; #ifdef _WINDOWS control = !!(::GetAsyncKeyState(VK_CONTROL) & 0x8000); #else control = m_control_key; #endif m_dump.VSync(field, !control, m_regs); } } // capture if(m_capture.IsCapturing()) { if(GSTexture* current = m_dev->GetCurrent()) { GSVector2i size = m_capture.GetSize(); if(GSTexture* offscreen = m_dev->CopyOffscreen(current, GSVector4(0, 0, 1, 1), size.x, size.y)) { GSTexture::GSMap m; if(offscreen->Map(m)) { m_capture.DeliverFrame(m.bits, m.pitch, !m_dev->IsRBSwapped()); offscreen->Unmap(); } m_dev->Recycle(offscreen); } } } }
bool GSRenderer::Merge(int field) { bool en[2]; GSVector4i fr[2]; GSVector4i dr[2]; int baseline = INT_MAX; for(int i = 0; i < 2; i++) { en[i] = IsEnabled(i); if(en[i]) { fr[i] = GetFrameRect(i); dr[i] = GetDisplayRect(i); baseline = min(dr[i].top, baseline); //printf("[%d]: %d %d %d %d, %d %d %d %d\n", i, fr[i].x,fr[i].y,fr[i].z,fr[i].w , dr[i].x,dr[i].y,dr[i].z,dr[i].w); } } if(!en[0] && !en[1]) { return false; } // try to avoid fullscreen blur, could be nice on tv but on a monitor it's like double vision, hurts my eyes (persona 4, guitar hero) // // NOTE: probably the technique explained in graphtip.pdf (Antialiasing by Supersampling / 4. Reading Odd/Even Scan Lines Separately with the PCRTC then Blending) bool samesrc = en[0] && en[1] && m_regs->DISP[0].DISPFB.FBP == m_regs->DISP[1].DISPFB.FBP && m_regs->DISP[0].DISPFB.FBW == m_regs->DISP[1].DISPFB.FBW && m_regs->DISP[0].DISPFB.PSM == m_regs->DISP[1].DISPFB.PSM; // bool blurdetected = false; if(samesrc /*&& m_regs->PMODE.SLBG == 0 && m_regs->PMODE.MMOD == 1 && m_regs->PMODE.ALP == 0x80*/) { if(fr[0].eq(fr[1] + GSVector4i(0, -1, 0, 0)) && dr[0].eq(dr[1] + GSVector4i(0, 0, 0, 1)) || fr[1].eq(fr[0] + GSVector4i(0, -1, 0, 0)) && dr[1].eq(dr[0] + GSVector4i(0, 0, 0, 1))) { // persona 4: // // fr[0] = 0 0 640 448 // fr[1] = 0 1 640 448 // dr[0] = 159 50 779 498 // dr[1] = 159 50 779 497 // // second image shifted up by 1 pixel and blended over itself // // god of war: // // fr[0] = 0 1 512 448 // fr[1] = 0 0 512 448 // dr[0] = 127 50 639 497 // dr[1] = 127 50 639 498 // // same just the first image shifted int top = min(fr[0].top, fr[1].top); int bottom = max(dr[0].bottom, dr[1].bottom); fr[0].top = top; fr[1].top = top; dr[0].bottom = bottom; dr[1].bottom = bottom; // blurdetected = true; } else if(dr[0].eq(dr[1]) && (fr[0].eq(fr[1] + GSVector4i(0, 1, 0, 1)) || fr[1].eq(fr[0] + GSVector4i(0, 1, 0, 1)))) { // dq5: // // fr[0] = 0 1 512 445 // fr[1] = 0 0 512 444 // dr[0] = 127 50 639 494 // dr[1] = 127 50 639 494 int top = min(fr[0].top, fr[1].top); int bottom = min(fr[0].bottom, fr[1].bottom); fr[0].top = fr[1].top = top; fr[0].bottom = fr[1].bottom = bottom; // blurdetected = true; } //printf("samesrc = %d blurdetected = %d\n",samesrc,blurdetected); } GSVector2i fs(0, 0); GSVector2i ds(0, 0); GSTexture* tex[2] = {NULL, NULL}; if(samesrc && fr[0].bottom == fr[1].bottom) { tex[0] = GetOutput(0); tex[1] = tex[0]; // saves one texture fetch } else { if(en[0]) tex[0] = GetOutput(0); if(en[1]) tex[1] = GetOutput(1); } GSVector4 src[2]; GSVector4 dst[2]; for(int i = 0; i < 2; i++) { if(!en[i] || !tex[i]) continue; GSVector4i r = fr[i]; // overscan hack if(dr[i].height() > 512) // hmm { int y = GetDeviceSize(i).y; if(m_regs->SMODE2.INT && m_regs->SMODE2.FFMD) y /= 2; r.bottom = r.top + y; } GSVector4 scale = GSVector4(tex[i]->GetScale()).xyxy(); src[i] = GSVector4(r) * scale / GSVector4(tex[i]->GetSize()).xyxy(); GSVector2 o(0, 0); if(dr[i].top - baseline >= 4) // 2? { o.y = tex[i]->GetScale().y * (dr[i].top - baseline); if(m_regs->SMODE2.INT && m_regs->SMODE2.FFMD) { o.y /= 2; } } dst[i] = GSVector4(o).xyxy() + scale * GSVector4(r.rsize()); fs.x = max(fs.x, (int)(dst[i].z + 0.5f)); fs.y = max(fs.y, (int)(dst[i].w + 0.5f)); } ds = fs; if(m_regs->SMODE2.INT && m_regs->SMODE2.FFMD) { ds.y *= 2; } bool slbg = m_regs->PMODE.SLBG; bool mmod = m_regs->PMODE.MMOD; if(tex[0] || tex[1]) { if(tex[0] == tex[1] && !slbg && (src[0] == src[1] & dst[0] == dst[1]).alltrue()) { // the two outputs are identical, skip drawing one of them (the one that is alpha blended) tex[0] = NULL; } GSVector4 c = GSVector4((int)m_regs->BGCOLOR.R, (int)m_regs->BGCOLOR.G, (int)m_regs->BGCOLOR.B, (int)m_regs->PMODE.ALP) / 255; m_dev->Merge(tex, src, dst, fs, slbg, mmod, c); if(m_regs->SMODE2.INT && m_interlace > 0) { if (m_interlace == 7 && m_regs->SMODE2.FFMD == 1) // Auto interlace enabled / Odd frame interlace setting { int field2 = 0; int mode = 2; m_dev->Interlace(ds, field ^ field2, mode, tex[1] ? tex[1]->GetScale().y : tex[0]->GetScale().y); } else { int field2 = 1 - ((m_interlace - 1) & 1); int mode = (m_interlace - 1) >> 1; m_dev->Interlace(ds, field ^ field2, mode, tex[1] ? tex[1]->GetScale().y : tex[0]->GetScale().y); } } if(m_shadeboost) { m_dev->ShadeBoost(); } if (m_shaderfx) { m_dev->ExternalFX(); } if(m_fxaa) { m_dev->FXAA(); } } return true; }
void GSRendererSW::VSync(int field) { Sync(0); // IncAge might delete a cached texture in use if(0) if(LOG) { fprintf(s_fp, "%lld\n", m_perfmon.GetFrame()); GSVector4i dr = GetDisplayRect(); GSVector4i fr = GetFrameRect(); GSVector2i ds = GetDeviceSize(); fprintf(s_fp, "dr %d %d %d %d, fr %d %d %d %d, ds %d %d\n", dr.x, dr.y, dr.z, dr.w, fr.x, fr.y, fr.z, fr.w, ds.x, ds.y); for(int i = 0; i < 2; i++) { if(i == 0 && !m_regs->PMODE.EN1) continue; if(i == 1 && !m_regs->PMODE.EN2) continue; fprintf(s_fp, "DISPFB[%d] BP=%05x BW=%d PSM=%d DBX=%d DBY=%d\n", i, m_regs->DISP[i].DISPFB.Block(), m_regs->DISP[i].DISPFB.FBW, m_regs->DISP[i].DISPFB.PSM, m_regs->DISP[i].DISPFB.DBX, m_regs->DISP[i].DISPFB.DBY ); fprintf(s_fp, "DISPLAY[%d] DX=%d DY=%d DW=%d DH=%d MAGH=%d MAGV=%d\n", i, m_regs->DISP[i].DISPLAY.DX, m_regs->DISP[i].DISPLAY.DY, m_regs->DISP[i].DISPLAY.DW, m_regs->DISP[i].DISPLAY.DH, m_regs->DISP[i].DISPLAY.MAGH, m_regs->DISP[i].DISPLAY.MAGV ); } fprintf(s_fp, "PMODE EN1=%d EN2=%d CRTMD=%d MMOD=%d AMOD=%d SLBG=%d ALP=%d\n", m_regs->PMODE.EN1, m_regs->PMODE.EN2, m_regs->PMODE.CRTMD, m_regs->PMODE.MMOD, m_regs->PMODE.AMOD, m_regs->PMODE.SLBG, m_regs->PMODE.ALP ); fprintf(s_fp, "SMODE1 CLKSEL=%d CMOD=%d EX=%d GCONT=%d LC=%d NVCK=%d PCK2=%d PEHS=%d PEVS=%d PHS=%d PRST=%d PVS=%d RC=%d SINT=%d SLCK=%d SLCK2=%d SPML=%d T1248=%d VCKSEL=%d VHP=%d XPCK=%d\n", m_regs->SMODE1.CLKSEL, m_regs->SMODE1.CMOD, m_regs->SMODE1.EX, m_regs->SMODE1.GCONT, m_regs->SMODE1.LC, m_regs->SMODE1.NVCK, m_regs->SMODE1.PCK2, m_regs->SMODE1.PEHS, m_regs->SMODE1.PEVS, m_regs->SMODE1.PHS, m_regs->SMODE1.PRST, m_regs->SMODE1.PVS, m_regs->SMODE1.RC, m_regs->SMODE1.SINT, m_regs->SMODE1.SLCK, m_regs->SMODE1.SLCK2, m_regs->SMODE1.SPML, m_regs->SMODE1.T1248, m_regs->SMODE1.VCKSEL, m_regs->SMODE1.VHP, m_regs->SMODE1.XPCK ); fprintf(s_fp, "SMODE2 INT=%d FFMD=%d DPMS=%d\n", m_regs->SMODE2.INT, m_regs->SMODE2.FFMD, m_regs->SMODE2.DPMS ); fprintf(s_fp, "SRFSH %08x_%08x\n", m_regs->SRFSH.u32[0], m_regs->SRFSH.u32[1] ); fprintf(s_fp, "SYNCH1 %08x_%08x\n", m_regs->SYNCH1.u32[0], m_regs->SYNCH1.u32[1] ); fprintf(s_fp, "SYNCH2 %08x_%08x\n", m_regs->SYNCH2.u32[0], m_regs->SYNCH2.u32[1] ); fprintf(s_fp, "SYNCV %08x_%08x\n", m_regs->SYNCV.u32[0], m_regs->SYNCV.u32[1] ); fprintf(s_fp, "CSR %08x_%08x\n", m_regs->CSR.u32[0], m_regs->CSR.u32[1] ); fflush(s_fp); } /* int draw[8], sum = 0; for(size_t i = 0; i < countof(draw); i++) { draw[i] = m_perfmon.CPU(GSPerfMon::WorkerDraw0 + i); sum += draw[i]; } printf("CPU %d Sync %d W %d %d %d %d %d %d %d %d (%d)\n", m_perfmon.CPU(GSPerfMon::Main), m_perfmon.CPU(GSPerfMon::Sync), draw[0], draw[1], draw[2], draw[3], draw[4], draw[5], draw[6], draw[7], sum); // */ GSRenderer::VSync(field); m_tc->IncAge(); // if((m_perfmon.GetFrame() & 255) == 0) m_rl->PrintStats(); }
void GSRendererHW::SetScaling() { GSVector2i crtc_size(GetDisplayRect().width(), GetDisplayRect().height()); // Details of (potential) perf impact of a big framebuffer // 1/ extra memory // 2/ texture cache framebuffer rescaling/copy // 3/ upload of framebuffer (preload hack) // 4/ framebuffer clear (color/depth/stencil) // 5/ read back of the frambuffer // 6/ MSAA // // With the solution // 1/ Nothing to do.Except the texture cache bug (channel shuffle effect) // most of the market is 1GB of VRAM (and soon 2GB) // 2/ limit rescaling/copy to the valid data of the framebuffer // 3/ ??? no solution so far // 4a/ stencil can be limited to valid data. // 4b/ is it useful to clear color? depth? (in any case, it ought to be few operation) // 5/ limit the read to the valid data // 6/ not support on openGL // Framebuffer width is always a multiple of 64 so at certain cases it can't cover some weird width values. // 480P , 576P use width as 720 which is not referencable by FBW * 64. so it produces 704 ( the closest value multiple by 64). // In such cases, let's just use the CRTC width. int fb_width = max({ (int)m_context->FRAME.FBW * 64, crtc_size.x , 512 }); // GS doesn't have a specific register for the FrameBuffer height. so we get the height // from physical units of the display rectangle in case the game uses a heigher value of height. // // Gregory: the framebuffer must have enough room to draw // * at least 2 frames such as FMV (see OI_BlitFMV) // * high resolution game such as snowblind engine game // // Autodetection isn't a good idea because it will create flickering // If memory consumption is an issue, there are 2 possibilities // * 1/ Avoid to create hundreds of RT // * 2/ Use sparse texture (requires recent HW) // // Avoid to alternate between 640x1280 and 1280x1024 on snow blind engine game // int fb_height = (fb_width < 1024) ? 1280 : 1024; // // Until performance issue is properly fixed, let's keep an option to reduce the framebuffer size. int fb_height = m_large_framebuffer ? 1280 : (fb_width < 1024) ? max(512, crtc_size.y) : 1024; int upscaled_fb_w = fb_width * m_upscale_multiplier; int upscaled_fb_h = fb_height * m_upscale_multiplier; bool good_rt_size = m_width >= upscaled_fb_w && m_height >= upscaled_fb_h; bool initialized_register_state = (m_context->FRAME.FBW > 1) && (crtc_size.y > 1); if (!m_upscale_multiplier && initialized_register_state) { if (m_height == m_custom_height) { float ratio = ceil(static_cast<float>(m_height) / crtc_size.y); float buffer_scale_offset = (m_large_framebuffer) ? ratio : 0.5f; ratio = round(ratio + buffer_scale_offset); m_tc->RemovePartial(); m_width = max(m_width, 1280); m_height = max(static_cast<int>(crtc_size.y * ratio) , 1024); } } // No need to resize for native/custom resolutions as default size will be enough for native and we manually get RT Buffer size for custom. // don't resize until the display rectangle and register states are stabilized. if ( m_upscale_multiplier <= 1 || good_rt_size) return; m_tc->RemovePartial(); m_width = upscaled_fb_w; m_height = upscaled_fb_h; printf("Frame buffer size set to %dx%d (%dx%d)\n", fb_width, fb_height , m_width, m_height); }