void Display(void) { int im_size_2 = im_size / 2; glClear(GL_COLOR_BUFFER_BIT); // rysowanie pikseli od lewego górnego narożnika do prawego dolnego narożnika DrawPixels(-im_size_2, im_size_2, im_size_2, 0); DrawPixels(-im_size_2, 0, im_size_2, -im_size_2); glFlush(); }
void FramebufferManagerCommon::UpdateFromMemory(u32 addr, int size, bool safe) { addr &= ~0x40000000; // TODO: Could go through all FBOs, but probably not important? // TODO: Could also check for inner changes, but video is most important. bool isDisplayBuf = addr == DisplayFramebufAddr() || addr == PrevDisplayFramebufAddr(); if (isDisplayBuf || safe) { // TODO: Deleting the FBO is a heavy hammer solution, so let's only do it if it'd help. if (!Memory::IsValidAddress(displayFramebufPtr_)) return; for (size_t i = 0; i < vfbs_.size(); ++i) { VirtualFramebuffer *vfb = vfbs_[i]; if (MaskedEqual(vfb->fb_address, addr)) { FlushBeforeCopy(); if (useBufferedRendering_ && vfb->fbo) { DisableState(); GEBufferFormat fmt = vfb->format; if (vfb->last_frame_render + 1 < gpuStats.numFlips && isDisplayBuf) { // If we're not rendering to it, format may be wrong. Use displayFormat_ instead. fmt = displayFormat_; } DrawPixels(vfb, 0, 0, Memory::GetPointer(addr | 0x04000000), fmt, vfb->fb_stride, vfb->width, vfb->height); SetColorUpdated(vfb); } else { INFO_LOG(SCEGE, "Invalidating FBO for %08x (%i x %i x %i)", vfb->fb_address, vfb->width, vfb->height, vfb->format); DestroyFramebuf(vfb); vfbs_.erase(vfbs_.begin() + i--); } } } RebindFramebuffer(); } }
// Step 4: the Window Procedure LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { HINSTANCE hInstance = GetModuleHandle(NULL); switch(msg) { case WM_CREATE: hBitmap = (HBITMAP) LoadImageW(NULL, L"sticker.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE); if (hBitmap == NULL) { MessageBoxW(hwnd, L"Failed to load image", L"Error", MB_OK); } break; case WM_PAINT: DrawPixels(hwnd); break; case WM_CLOSE: DestroyWindow(hwnd); break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hwnd, msg, wParam, lParam); } return 0; }
bool CVobSubImage::Decode(BYTE* lpData, int packetsize, int datasize, bool fCustomPal, int tridx, RGBQUAD* orgpal /*[16]*/, RGBQUAD* cuspal /*[4]*/, bool fTrim) { GetPacketInfo(lpData, packetsize, datasize); if(!Alloc(rect.Width(), rect.Height())) { return(false); } lpPixels = lpTemp1; nPlane = 0; fAligned = 1; this->fCustomPal = fCustomPal; this->orgpal = orgpal; this->tridx = tridx; this->cuspal = cuspal; CPoint p(rect.left, rect.top); int end0 = nOffset[1]; int end1 = datasize; while((nPlane == 0 && nOffset[0] < end0) || (nPlane == 1 && nOffset[1] < end1)) { DWORD code; if((code = GetNibble(lpData)) >= 0x4 || (code = (code << 4) | GetNibble(lpData)) >= 0x10 || (code = (code << 4) | GetNibble(lpData)) >= 0x40 || (code = (code << 4) | GetNibble(lpData)) >= 0x100) { DrawPixels(p, code >> 2, code & 3); if((p.x += code >> 2) < rect.right) { continue; } } DrawPixels(p, rect.right - p.x, code & 3); if(!fAligned) { GetNibble(lpData); // align to byte } p.x = rect.left; p.y++; nPlane = 1 - nPlane; }
void FramebufferManagerCommon::NotifyBlockTransferAfter(u32 dstBasePtr, int dstStride, int dstX, int dstY, u32 srcBasePtr, int srcStride, int srcX, int srcY, int width, int height, int bpp, u32 skipDrawReason) { // A few games use this INSTEAD of actually drawing the video image to the screen, they just blast it to // the backbuffer. Detect this and have the framebuffermanager draw the pixels. u32 backBuffer = PrevDisplayFramebufAddr(); u32 displayBuffer = DisplayFramebufAddr(); // TODO: Is this not handled by upload? Should we check !dstBuffer to avoid a double copy? if (((backBuffer != 0 && dstBasePtr == backBuffer) || (displayBuffer != 0 && dstBasePtr == displayBuffer)) && dstStride == 512 && height == 272 && !useBufferedRendering_) { FlushBeforeCopy(); DrawFramebufferToOutput(Memory::GetPointerUnchecked(dstBasePtr), displayFormat_, 512, false); } if (MayIntersectFramebuffer(srcBasePtr) || MayIntersectFramebuffer(dstBasePtr)) { VirtualFramebuffer *dstBuffer = 0; VirtualFramebuffer *srcBuffer = 0; int srcWidth = width; int srcHeight = height; int dstWidth = width; int dstHeight = height; FindTransferFramebuffers(dstBuffer, srcBuffer, dstBasePtr, dstStride, dstX, dstY, srcBasePtr, srcStride, srcX, srcY, srcWidth, srcHeight, dstWidth, dstHeight, bpp); if (!useBufferedRendering_ && currentRenderVfb_ != dstBuffer) { return; } if (dstBuffer && !srcBuffer) { WARN_LOG_ONCE(btu, G3D, "Block transfer upload %08x -> %08x", srcBasePtr, dstBasePtr); if (g_Config.bBlockTransferGPU) { FlushBeforeCopy(); const u8 *srcBase = Memory::GetPointerUnchecked(srcBasePtr) + (srcX + srcY * srcStride) * bpp; int dstBpp = dstBuffer->format == GE_FORMAT_8888 ? 4 : 2; float dstXFactor = (float)bpp / dstBpp; if (dstWidth > dstBuffer->width || dstHeight > dstBuffer->height) { // The buffer isn't big enough, and we have a clear hint of size. Resize. // This happens in Valkyrie Profile when uploading video at the ending. ResizeFramebufFBO(dstBuffer, dstWidth, dstHeight, false, true); } DrawPixels(dstBuffer, static_cast<int>(dstX * dstXFactor), dstY, srcBase, dstBuffer->format, static_cast<int>(srcStride * dstXFactor), static_cast<int>(dstWidth * dstXFactor), dstHeight); SetColorUpdated(dstBuffer, skipDrawReason); RebindFramebuffer(); } } } }
// функция получает два отрезка и закрашивает пространство между ними // заранее известно, что у каждого отрезка первая точка имеет Y не больше чем вторая // а также p левее q inline static void FillInside(const Segment& p, const Segment& q, const Color& color, const SDLApplication* app, Scene3D* scene) { const int ystart = std::max(0, static_cast<int>(ceil(std::max(p.first.p[1], q.first.p[1])))); const int yfinish = std::min(app->Screen->h - 1, static_cast<int>(floor(std::min(p.second.p[1], q.second.p[1])))); // d##_dy - это производная ## по y (константа, так как эти переменные линейны по x и по y), смысл переменных ## - объяснен ниже // если отрезок p горизонтален, то dpw_dy, dpx_dy не имеет смысла, так что пусть будет 0 const double dpw_dy = (p.second.p[1] == p.first.p[1]) ? 0 : (p.second.w - p.first.w) / (p.second.p[1] - p.first.p[1]); const double dpx_dy = (p.second.p[1] == p.first.p[1]) ? 0 : (p.second.p[0] - p.first.p[0]) / (p.second.p[1] - p.first.p[1]); // если отрезок q горизонтален, то dqw_dy, dqx_dy не имеет смысла, так что пусть будет 0 const double dqw_dy = (q.second.p[1] == q.first.p[1]) ? 0 : (q.second.w - q.first.w) / (q.second.p[1] - q.first.p[1]); const double dqx_dy = (q.second.p[1] == q.first.p[1]) ? 0 : (q.second.p[0] - q.first.p[0]) / (q.second.p[1] - q.first.p[1]); // нахождение dw_dx // кроме вырожденных случаев, только на одном y выполняется deltax = qx - px == 0 (в точке пересечения) // так как dw_dx не зависит от y, то можно сложить равенства deltaw = deltax * dw_dx, для двух разных y // и получить (deltaw1 + deltaw2) = (deltax1 + deltax2) * dw_dx // dw_dx = (deltaw1 + deltaw2) / (deltax1 + deltax2) // возьмем в качестве двух разных y ystart и yfinish // в знаменателе будет 0 только в вырожденном случае (если ystart = yfinish = ординате точки пересечения) // в этом случае рисуем только одну точку и dw_dx не имеет значения const double deltax1 = (q.first.p[0] + (ystart - q.first.p[1]) * dqx_dy) - (p.first.p[0] + (ystart - p.first.p[1]) * dpx_dy); const double deltax2 = (q.first.p[0] + (yfinish - q.first.p[1]) * dqx_dy) - (p.first.p[0] + (yfinish - p.first.p[1]) * dpx_dy); const double deltaw1 = (q.first.w + (ystart - q.first.p[1]) * dqw_dy) - (p.first.w + (ystart - p.first.p[1]) * dpw_dy); const double deltaw2 = (q.first.w + (yfinish - q.first.p[1]) * dqw_dy) - (p.first.w + (yfinish - p.first.p[1]) * dpw_dy); const double dw_dx = (deltax1 + deltax2 == 0) ? 0 : (deltaw1 + deltaw2) / (deltax1 + deltax2); // pw - это значение w в точке пересечения прямой, содержащей отрезок p и прямой Y = y, сейчас Y = ystart double pw = p.first.w + (ystart - p.first.p[1]) * dpw_dy; // px - это значение x в точке пересечения прямой, содержащей отрезок p и прямой Y = y, сейчас Y = ystart double px = p.first.p[0] + (ystart - p.first.p[1]) * dpx_dy; // qx - это значение x в точке пересечения прямой, содержащей отрезок q и прямой Y = y, сейчас Y = ystart double qx = q.first.p[0] + (ystart - q.first.p[1]) * dqx_dy; for(int y = ystart; y <= yfinish; ++y) { DrawPixels(px, qx, pw, dw_dx, y, color, app, scene); pw += dpw_dy; px += dpx_dy; qx += dqx_dy; } }
bool FramebufferManagerCommon::NotifyFramebufferCopy(u32 src, u32 dst, int size, bool isMemset) { if (updateVRAM_ || size == 0) { return false; } dst &= 0x3FFFFFFF; src &= 0x3FFFFFFF; VirtualFramebuffer *dstBuffer = 0; VirtualFramebuffer *srcBuffer = 0; u32 dstY = (u32)-1; u32 dstH = 0; u32 srcY = (u32)-1; u32 srcH = 0; for (size_t i = 0; i < vfbs_.size(); ++i) { VirtualFramebuffer *vfb = vfbs_[i]; if (vfb->fb_stride == 0) { continue; } const u32 vfb_address = (0x04000000 | vfb->fb_address) & 0x3FFFFFFF; const u32 vfb_size = FramebufferByteSize(vfb); const u32 vfb_bpp = vfb->format == GE_FORMAT_8888 ? 4 : 2; const u32 vfb_byteStride = vfb->fb_stride * vfb_bpp; const int vfb_byteWidth = vfb->width * vfb_bpp; if (dst >= vfb_address && (dst + size <= vfb_address + vfb_size || dst == vfb_address)) { const u32 offset = dst - vfb_address; const u32 yOffset = offset / vfb_byteStride; if ((offset % vfb_byteStride) == 0 && (size == vfb_byteWidth || (size % vfb_byteStride) == 0) && yOffset < dstY) { dstBuffer = vfb; dstY = yOffset; dstH = size == vfb_byteWidth ? 1 : std::min((u32)size / vfb_byteStride, (u32)vfb->height); } } if (src >= vfb_address && (src + size <= vfb_address + vfb_size || src == vfb_address)) { const u32 offset = src - vfb_address; const u32 yOffset = offset / vfb_byteStride; if ((offset % vfb_byteStride) == 0 && (size == vfb_byteWidth || (size % vfb_byteStride) == 0) && yOffset < srcY) { srcBuffer = vfb; srcY = yOffset; srcH = size == vfb_byteWidth ? 1 : std::min((u32)size / vfb_byteStride, (u32)vfb->height); } else if ((offset % vfb_byteStride) == 0 && size == vfb->fb_stride && yOffset < srcY) { // Valkyrie Profile reads 512 bytes at a time, rather than 2048. So, let's whitelist fb_stride also. srcBuffer = vfb; srcY = yOffset; srcH = 1; } } } if (srcBuffer && srcY == 0 && srcH == srcBuffer->height && !dstBuffer) { // MotoGP workaround - it copies a framebuffer to memory and then displays it. // TODO: It's rare anyway, but the game could modify the RAM and then we'd display the wrong thing. // Unfortunately, that would force 1x render resolution. if (Memory::IsRAMAddress(dst)) { knownFramebufferRAMCopies_.insert(std::pair<u32, u32>(src, dst)); } } if (!useBufferedRendering_) { // If we're copying into a recently used display buf, it's probably destined for the screen. if (srcBuffer || (dstBuffer != displayFramebuf_ && dstBuffer != prevDisplayFramebuf_)) { return false; } } if (dstBuffer && srcBuffer && !isMemset) { if (srcBuffer == dstBuffer) { WARN_LOG_REPORT_ONCE(dstsrccpy, G3D, "Intra-buffer memcpy (not supported) %08x -> %08x", src, dst); } else { WARN_LOG_REPORT_ONCE(dstnotsrccpy, G3D, "Inter-buffer memcpy %08x -> %08x", src, dst); // Just do the blit! if (g_Config.bBlockTransferGPU) { BlitFramebuffer(dstBuffer, 0, dstY, srcBuffer, 0, srcY, srcBuffer->width, srcH, 0); SetColorUpdated(dstBuffer); RebindFramebuffer(); } } return false; } else if (dstBuffer) { WARN_LOG_ONCE(btucpy, G3D, "Memcpy fbo upload %08x -> %08x", src, dst); if (g_Config.bBlockTransferGPU) { FlushBeforeCopy(); const u8 *srcBase = Memory::GetPointerUnchecked(src); DrawPixels(dstBuffer, 0, dstY, srcBase, dstBuffer->format, dstBuffer->fb_stride, dstBuffer->width, dstH); SetColorUpdated(dstBuffer); RebindFramebuffer(); // This is a memcpy, let's still copy just in case. return false; } return false; } else if (srcBuffer) { WARN_LOG_ONCE(btdcpy, G3D, "Memcpy fbo download %08x -> %08x", src, dst); FlushBeforeCopy(); if (srcH == 0 || srcY + srcH > srcBuffer->bufferHeight) { WARN_LOG_REPORT_ONCE(btdcpyheight, G3D, "Memcpy fbo download %08x -> %08x skipped, %d+%d is taller than %d", src, dst, srcY, srcH, srcBuffer->bufferHeight); } else if (g_Config.bBlockTransferGPU && !srcBuffer->memoryUpdated) { ReadFramebufferToMemory(srcBuffer, true, 0, srcY, srcBuffer->width, srcH); } return false; } else { return false; } }
void DrawScene(void) { /* clear the draw buffer */ glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT); // clear the rasterization framebuffer memset(framebuffer, 0, 3*framebuffer_width*framebuffer_height); if (scene == 1) DrawTriangles(); else if (scene == 2) TestRasterizationSpeed(); else if (scene == 4) DrawPixels(); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); if (scene != 9) { if (zoom) glOrtho(0, framebuffer_width, 0, framebuffer_height, -1, 1); else glOrtho(0, screen_width, 0, screen_height, -1, 1); // Draw textured quad glEnable(GL_TEXTURE_2D); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, framebuffer_width, framebuffer_height, 0, GL_RGB, GL_UNSIGNED_BYTE, framebuffer); glColor3f(1, 1, 1); glBegin(GL_QUADS); glTexCoord2i(0, 0); glVertex2i(0, 0); glTexCoord2i(1, 0); glVertex2i(framebuffer_width, 0); glTexCoord2i(1, 1); glVertex2i(framebuffer_width, framebuffer_height); glTexCoord2i(0, 1); glVertex2i(0, framebuffer_height); glEnd(); } else { if (zoom) glOrtho(-0.5, framebuffer_width-0.5, -0.5, framebuffer_height-0.5, -1, 1); else glOrtho(-0.5, screen_width-0.5, -0.5, screen_height-0.5, -1, 1); DrawTrianglesOpenGL(); glDisable(GL_TEXTURE_2D); glColor3f(1, 1, 0); glBegin(GL_POINTS); glVertex2i(0, 0); glVertex2i(framebuffer_width-1, framebuffer_height-1); glEnd(); } // finally, swap the draw buffers to make the triangles appear on screen glutSwapBuffers(); }