diff --git a/configuration.c b/configuration.c index 93863c4d535d..c0dd3e79e9dd 100644 --- a/configuration.c +++ b/configuration.c @@ -1850,6 +1850,7 @@ static struct config_bool_setting *populate_settings_bool( SETTING_BOOL("crt_switch_resolution_use_custom_refresh_rate", &settings->bools.crt_switch_custom_refresh_enable, true, false, false); SETTING_BOOL("crt_switch_hires_menu", &settings->bools.crt_switch_hires_menu, true, false, true); + SETTING_BOOL("mister_force_scaler", &settings->bools.mister_force_scaler, true, false, true); SETTING_BOOL("mister_scanlines", &settings->bools.mister_scanlines, true, false, true); SETTING_BOOL("mister_force_rgb565", &settings->bools.mister_force_rgb565, true, false, true); SETTING_BOOL("mister_interlaced_fb", &settings->bools.mister_interlaced_fb, true, true, true); diff --git a/configuration.h b/configuration.h index 7fcd93f2fcf4..699a88339814 100644 --- a/configuration.h +++ b/configuration.h @@ -883,6 +883,7 @@ typedef struct settings bool crt_switch_custom_refresh_enable; bool crt_switch_hires_menu; + bool mister_force_scaler; bool mister_scanlines; bool mister_force_rgb565; bool mister_interlaced_fb; diff --git a/deps/mister/groovymister.cpp b/deps/mister/groovymister.cpp index 13bc87e3c83a..b53a9a6611a5 100644 --- a/deps/mister/groovymister.cpp +++ b/deps/mister/groovymister.cpp @@ -36,6 +36,7 @@ #define CMD_GET_STATUS 5 #define CMD_BLIT_VSYNC 6 #define CMD_BLIT_FIELD_VSYNC 7 +#define CMD_GET_VERSION 8 typedef union { @@ -107,6 +108,10 @@ GroovyMister::GroovyMister() m_emulationTime = 0; m_mtu = 0; m_doCongestionControl = 0; + m_network_ping = 0; + m_delta_enabled[0] = 0; + m_delta_enabled[1] = 0; + m_isConnected = 0; memset(&m_tickStart, 0, sizeof(m_tickStart)); memset(&m_tickEnd, 0, sizeof(m_tickEnd)); @@ -115,37 +120,71 @@ GroovyMister::GroovyMister() DWORD totalBufferCount = 0; DWORD totalBufferSize = 0; - pBufferBlit = AllocateBufferSpace(BUFFER_SIZE, 1, totalBufferSize, totalBufferCount); - pBufferAudio = AllocateBufferSpace(BUFFER_SIZE, 1, totalBufferSize, totalBufferCount); - m_pBufferLZ4 = AllocateBufferSpace(BUFFER_SIZE, 1, totalBufferSize, totalBufferCount); + m_pBufferAudio = AllocateBufferSpace(BUFFER_SIZE, 1, totalBufferSize, totalBufferCount); + m_pBufferBlitDelta = AllocateBufferSpace(BUFFER_SIZE, 1, totalBufferSize, totalBufferCount); + for(int i=0;i<2;i++) + { + m_pBufferBlit[i] = AllocateBufferSpace(BUFFER_SIZE, 1, totalBufferSize, totalBufferCount); + m_pBufferLZ4[i] = AllocateBufferSpace(BUFFER_SIZE, 1, totalBufferSize, totalBufferCount); + } } GroovyMister::~GroovyMister() { #ifdef _WIN32 - VirtualFree(pBufferBlit, 0, MEM_RELEASE); - VirtualFree(pBufferAudio, 0, MEM_RELEASE); - VirtualFree(m_pBufferLZ4, 0, MEM_RELEASE); + VirtualFree(m_pBufferAudio, 0, MEM_RELEASE); + VirtualFree(m_pBufferBlitDelta, 0, MEM_RELEASE); + for(int i=0;i<2;i++) + { + VirtualFree(m_pBufferBlit[i], 0, MEM_RELEASE); + VirtualFree(m_pBufferLZ4[i], 0, MEM_RELEASE); + } #else - free(pBufferBlit); - free(pBufferAudio); - free(m_pBufferLZ4); + free(m_pBufferAudio); + free(m_pBufferBlitDelta); + for(int i=0;i<2;i++) + { + free(m_pBufferBlit[i]); + free(m_pBufferLZ4[i]); + } #endif } +char* GroovyMister::getPBufferBlit(uint8_t field) +{ + return m_pBufferBlit[field]; +} + +char* GroovyMister::getPBufferBlitDelta(void) +{ + return m_pBufferBlitDelta; +} + +char* GroovyMister::getPBufferAudio(void) +{ + return m_pBufferAudio; +} + void GroovyMister::CmdClose(void) { - m_bufferSend[0] = CMD_CLOSE; - Send(&m_bufferSend[0], 1); + if (m_isConnected) + { + m_bufferSend[0] = CMD_CLOSE; + Send(&m_bufferSend[0], 1); + } #ifdef _WIN32 if (USE_RIO) { m_rio.RIOCloseCompletionQueue(m_sendQueue); m_rio.RIOCloseCompletionQueue(m_receiveQueue); m_rio.RIODeregisterBuffer(m_sendRioBufferId); - m_rio.RIODeregisterBuffer(m_sendRioBufferBlitId); m_rio.RIODeregisterBuffer(m_sendRioBufferAudioId); + for (int i=0;i<2;i++) + { + m_rio.RIODeregisterBuffer(m_sendRioBufferBlitId[i]); + } + } ::closesocket(m_sockFD); ::closesocket(m_sockInputsFD); @@ -166,8 +205,9 @@ const char* GroovyMister::getVersion() return &GROOVYMISTER_VERSION[0]; } -uint8_t GroovyMister::CmdInit(const char* misterHost, uint16_t misterPort, uint8_t lz4Frames, uint32_t soundRate, uint8_t soundChan, uint8_t rgbMode, uint16_t mtu) +int GroovyMister::CmdInit(const char* misterHost, uint16_t misterPort, int lz4Frames, uint32_t soundRate, uint8_t soundChan, uint8_t rgbMode, uint16_t mtu) { + m_isConnected = 0; m_mtu = (!mtu) ? BUFFER_MTU : mtu - MTU_HEADER; // Set server @@ -184,6 +224,7 @@ uint8_t GroovyMister::CmdInit(const char* misterHost, uint16_t misterPort, uint8 if (rc != 0) { LOG(0, "[MiSTer] Unable to load Winsock: %d\n", rc); + return -1; } m_sockFD = INVALID_SOCKET; @@ -195,6 +236,7 @@ uint8_t GroovyMister::CmdInit(const char* misterHost, uint16_t misterPort, uint8 if (m_sockFD == INVALID_SOCKET) { LOG(0,"[MiSTer] Could not create socket : %lu", ::GetLastError()); + return -1; } DWORD val = 1; @@ -202,6 +244,7 @@ uint8_t GroovyMister::CmdInit(const char* misterHost, uint16_t misterPort, uint8 if (rc != 0) { LOG(0,"[MiSTer] Could not create IP_DONTFRAGMENT : %lu", ::GetLastError()); + return -1; } LOG(0,"[MiSTer] Setting WSAIoctl %s...\n",""); @@ -210,12 +253,14 @@ uint8_t GroovyMister::CmdInit(const char* misterHost, uint16_t misterPort, uint8 if ( 0 != WSAIoctl(m_sockFD, SIO_GET_MULTIPLE_EXTENSION_FUNCTION_POINTER, &functionTableId, sizeof(GUID), (void**)&m_rio, sizeof(m_rio), &dwBytes, NULL, NULL) ) { LOG(0,"[MiSTer] Could not create WSAIoctl : %lu", ::GetLastError()); + return -1; } m_hIOCP = ::CreateIoCompletionPort(INVALID_HANDLE_VALUE, 0, 0, 0) ; if (NULL == m_hIOCP) { LOG(0,"[MiSTer] Could not create m_hIOCP IoCompletionPort : %lu", ::GetLastError()); + return -1; } OVERLAPPED overlapped; @@ -233,6 +278,7 @@ uint8_t GroovyMister::CmdInit(const char* misterHost, uint16_t misterPort, uint8 if (m_sendRioBufferId == RIO_INVALID_BUFFERID) { LOG(0,"[MiSTer] RIORegisterBuffer m_BufferSend Error: %lu\n", ::GetLastError()); + return -1; } m_sendRioBuffer.BufferId = m_sendRioBufferId; m_sendRioBuffer.Offset = 0; @@ -242,41 +288,47 @@ uint8_t GroovyMister::CmdInit(const char* misterHost, uint16_t misterPort, uint8 if (m_receiveRioBufferId == RIO_INVALID_BUFFERID) { LOG(0,"[MiSTer] RIORegisterBuffer m_BufferReceive Error: %lu\n", ::GetLastError()); + return -1; } m_receiveRioBuffer.BufferId = m_receiveRioBufferId; m_receiveRioBuffer.Offset = 0; m_receiveRioBuffer.Length = 17; - - if (lz4Frames) - { - m_sendRioBufferBlitId = m_rio.RIORegisterBuffer(m_pBufferLZ4, BUFFER_SIZE); - } - else - { - m_sendRioBufferBlitId = m_rio.RIORegisterBuffer(pBufferBlit, BUFFER_SIZE); - } - if (m_sendRioBufferBlitId == RIO_INVALID_BUFFERID) - { - LOG(0,"[MiSTer] RIORegisterBuffer pBufferBlit Error: %lu\n", ::GetLastError()); - } - + DWORD offset = 0; - m_pBufsBlit = new RIO_BUF[BUFFER_SLICES]; - for (DWORD i = 0; i < BUFFER_SLICES; ++i) + for (int field = 0; field < 2; field++) { - RIO_BUF *pBuffer = m_pBufsBlit + i; - - pBuffer->BufferId = m_sendRioBufferBlitId; - pBuffer->Offset = offset; - pBuffer->Length = m_mtu; - - offset += m_mtu; + if (lz4Frames) + { + m_sendRioBufferBlitId[field] = m_rio.RIORegisterBuffer(m_pBufferLZ4[field], BUFFER_SIZE); + } + else + { + m_sendRioBufferBlitId[field] = m_rio.RIORegisterBuffer(m_pBufferBlit[field], BUFFER_SIZE); + } + if (m_sendRioBufferBlitId[field] == RIO_INVALID_BUFFERID) + { + LOG(0,"[MiSTer] RIORegisterBuffer pBufferBlit[%d] Error: %lu\n", field, ::GetLastError()); + return -1; + } + + offset = 0; + m_pBufsBlit[field] = new RIO_BUF[BUFFER_SLICES]; + for (DWORD i = 0; i < BUFFER_SLICES; ++i) + { + RIO_BUF *pBuffer = m_pBufsBlit[field] + i; + + pBuffer->BufferId = m_sendRioBufferBlitId[field]; + pBuffer->Offset = offset; + pBuffer->Length = m_mtu; + + offset += m_mtu; + } } - - m_sendRioBufferAudioId = m_rio.RIORegisterBuffer(pBufferAudio, BUFFER_SIZE); + m_sendRioBufferAudioId = m_rio.RIORegisterBuffer(m_pBufferAudio, BUFFER_SIZE); if (m_sendRioBufferAudioId == RIO_INVALID_BUFFERID) { LOG(0,"[MiSTer] RIORegisterBuffer pBufferAudio Error: %lu\n", ::GetLastError()); + return -1; } offset = 0; m_pBufsAudio = new RIO_BUF[BUFFER_SLICES]; @@ -296,24 +348,28 @@ uint8_t GroovyMister::CmdInit(const char* misterHost, uint16_t misterPort, uint8 if (m_sendQueue == RIO_INVALID_CQ) { LOG(0,"[MiSTer]Could not create m_sendQueue : %lu", ::GetLastError()); + return -1; } m_receiveQueue = m_rio.RIOCreateCompletionQueue(BUFFER_SLICES, &completionType); if (m_receiveQueue == RIO_INVALID_CQ) { LOG(0,"[MiSTer]Could not create m_receiveQueue : %lu", ::GetLastError()); + return -1; } m_requestQueue = m_rio.RIOCreateRequestQueue(m_sockFD, BUFFER_SLICES, 1, BUFFER_SLICES, 1, m_receiveQueue, m_sendQueue, NULL) ; if (m_requestQueue == RIO_INVALID_RQ) { LOG(0,"[MiSTer]Could not create m_requestQueue : %lu", ::GetLastError()); + return -1; } LOG(0,"[MiSTer] Connect %s...\n",""); if (SOCKET_ERROR == ::connect(m_sockFD, reinterpret_cast(&m_serverAddr), sizeof(m_serverAddr))) { LOG(0,"[MiSTer] Could not connect : %lu", ::GetLastError()); + return -1; } m_rio.RIONotify(m_receiveQueue); @@ -325,6 +381,7 @@ uint8_t GroovyMister::CmdInit(const char* misterHost, uint16_t misterPort, uint8 if (m_sockFD == INVALID_SOCKET) { LOG(0,"[MiSTer] Could not create socket : %lu", ::GetLastError()); + return -1; } LOG(0,"[MiSTer] Setting socket async %s...\n",""); @@ -333,6 +390,7 @@ uint8_t GroovyMister::CmdInit(const char* misterHost, uint16_t misterPort, uint8 if (rc < 0) { LOG(0,"[MiSTer] set nonblock fail %d\n", rc); + return -1; } LOG(0,"[MiSTer] Setting send buffer to %d bytes...\n", 2097152); @@ -342,15 +400,17 @@ uint8_t GroovyMister::CmdInit(const char* misterHost, uint16_t misterPort, uint8 if (rc != 0) { LOG(0,"[MiSTer] Unable to set send buffer: %d\n", rc); + return -1; } } #else printf("[DEBUG] Initialising socket...\n"); m_sockFD = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); - if (sockfd < 0) + if (m_sockFD < 0) { - LOG(0,"[MiSTer] Could not create socket : %d", sockfd); + LOG(0,"[MiSTer] Could not create socket : %d", m_sockFD); + return -1; } LOG(0,"[MiSTer] Setting socket async %s...\n",""); @@ -360,11 +420,13 @@ uint8_t GroovyMister::CmdInit(const char* misterHost, uint16_t misterPort, uint8 if (flags < 0) { LOG(0,"[MiSTer] get falg error %d\n", flags); + return -1; } flags |= O_NONBLOCK; if (fcntl(m_sockFD, F_SETFL, flags) < 0) { LOG(0,"[MiSTer] set nonblock fail %d\n", flags); + return -1; } printf("[DEBUG] Setting send buffer to 2097152 bytes...\n"); @@ -372,6 +434,7 @@ uint8_t GroovyMister::CmdInit(const char* misterHost, uint16_t misterPort, uint8 if (setsockopt(m_sockFD, SOL_SOCKET, SO_SNDBUF, (void*)&size, sizeof(size)) < 0) { LOG(0,"[MiSTer] Unable to set send buffer: %d\n", 2097152); + return -1; } #endif @@ -401,18 +464,40 @@ uint8_t GroovyMister::CmdInit(const char* misterHost, uint16_t misterPort, uint8 { LOG(0,"[MiSTer] ACK failed with %d ms\n", 60); CmdClose(); - return 0; + return -1; } else { LOG(0,"[MiSTer] ACK received with %f ms\n", (double) ackTime / 10000); - return 1; + m_network_ping = 0; +/* + for (int i=0; i<10; i++) + { + m_bufferSend[0] = CMD_GET_VERSION; + Send(&m_bufferSend[0], 1); +#ifdef _WIN32 + if (USE_RIO) + { + m_rio.RIOReceive(m_requestQueue, &m_receiveRioBuffer, 1, 0, &m_receiveRioBuffer); + } +#endif + ackTime = getACK(60); + m_network_ping += ackTime; + } + m_network_ping = m_network_ping / 10; + LOG(0,"[MiSTer] Version %d received 10 times with ping %f ms\n", m_core_version, (double) m_network_ping / 10000); +*/ + m_isConnected = 1; + return 0; } } void GroovyMister::CmdSwitchres(double pClock, uint16_t hActive, uint16_t hBegin, uint16_t hEnd, uint16_t hTotal, uint16_t vActive, uint16_t vBegin, uint16_t vEnd, uint16_t vTotal, uint8_t interlace) { + if (!m_isConnected) + return; + uint8_t interlace_modeline = (interlace != 2) ? interlace : 1; m_RGBSize = (m_rgbMode == 1) ? (hActive * vActive) << 2 : (m_rgbMode == 2) ? (hActive * vActive) << 1 : hActive * vActive * 3; @@ -424,8 +509,11 @@ void GroovyMister::CmdSwitchres(double pClock, uint16_t hActive, uint16_t hBegin m_widthTime = 10 * round((double) hTotal * (1 / pClock)); //in nanosec, time to raster 1 line m_frameTime = (m_widthTime * vTotal) >> interlace_modeline; + m_interlace = interlace_modeline; m_vTotal = vTotal; + m_delta_enabled[0] = 0; + m_delta_enabled[1] = 0; m_bufferSend[0] = CMD_SWITCHRES; memcpy(&m_bufferSend[1],&pClock,sizeof(pClock)); @@ -442,8 +530,11 @@ void GroovyMister::CmdSwitchres(double pClock, uint16_t hActive, uint16_t hBegin Send(&m_bufferSend[0], 26); } -void GroovyMister::CmdBlit(uint32_t frame, uint8_t field, uint16_t vCountSync, uint32_t margin) +void GroovyMister::CmdBlit(uint32_t frame, uint8_t field, uint16_t vCountSync, uint32_t margin, uint32_t matchDeltaBytes) { + if (!m_isConnected) + return; + m_frame = frame; uint16_t vSync = vCountSync; @@ -455,28 +546,69 @@ void GroovyMister::CmdBlit(uint32_t frame, uint8_t field, uint16_t vCountSync, u } else { - uint32_t timeCalc = (margin + m_emulationTime >= m_frameTime) ? 0 : margin + m_emulationTime - m_streamTime; + uint32_t timeCalc = (m_network_ping + margin + m_emulationTime >= m_frameTime) ? 0 : m_network_ping + margin + m_emulationTime - m_streamTime; vSync = (timeCalc == 0) ? 1 : m_vTotal - round(m_vTotal * timeCalc) / m_frameTime; } } uint32_t cSize = 0; + uint32_t cSizeDelta = 0; uint32_t bytesToSend = 0; - - switch (m_lz4Frames) + double ratio_delta = 1.0; + if (m_lz4Frames) { - case(3): - case(1): cSize = LZ4_compress_default((char *)&pBufferBlit[0], m_pBufferLZ4, m_RGBSize, m_RGBSize); - break; - case(2): cSize = LZ4_compress_HC((char *)&pBufferBlit[0], m_pBufferLZ4, m_RGBSize, m_RGBSize, LZ4HC_CLEVEL_DEFAULT); - break; - } + double ratio_match = (double) matchDeltaBytes / m_RGBSize; + if (!(m_lz4Frames % 2 == 0) || ratio_match < 1 || !m_delta_enabled[field]) // duplicated frame, compress only delta + { + switch (m_lz4Frames) + { + case(6): + case(5): + case(2): + case(1): cSize = LZ4_compress_default((char *)&m_pBufferBlit[field][0], m_pBufferLZ4[0], m_RGBSize, m_RGBSize); + break; + case(4): + case(3): cSize = LZ4_compress_HC((char *)&m_pBufferBlit[field][0], m_pBufferLZ4[0], m_RGBSize, m_RGBSize, LZ4HC_CLEVEL_DEFAULT); + break; + } + } + else + { + cSize = m_RGBSize; + } + cSizeDelta = cSize; + double ratio_lz4 = (double) cSize / m_RGBSize; + if ((m_lz4Frames % 2 == 0) && m_delta_enabled[field] && ratio_lz4 > 0.05 && ratio_match > 0.20 && ratio_match > 0.9 - ratio_lz4) // try_delta size + { + switch (m_lz4Frames) + { + case(6): + case(5): + case(2): + case(1): cSizeDelta = LZ4_compress_default((char *)&m_pBufferBlitDelta[0], m_pBufferLZ4[1], m_RGBSize, m_RGBSize); + break; + case(4): + case(3): cSizeDelta = LZ4_compress_HC((char *)&m_pBufferBlitDelta[0], m_pBufferLZ4[1], m_RGBSize, m_RGBSize, LZ4HC_CLEVEL_DEFAULT); + break; + } + ratio_delta = (double) cSizeDelta / cSize; + //LOG(0,"frame %d raw %d, match %d, ratio %f csize %d cSizeDelta %d ratio_delta %f\n",frame, m_RGBSize, matchDeltaBytes, ratio_match, cSize, cSizeDelta, ratio_delta); + } - if (m_lz4Frames == 3 && cSize > LZ4_ADAPTATIVE_CSIZE) - { - cSize = LZ4_compress_HC((char *)&pBufferBlit[0], m_pBufferLZ4, m_RGBSize, m_RGBSize, LZ4HC_CLEVEL_DEFAULT); - m_lz4Frames = 2; - LOG(0,"[MiSTer] LZ4 Adaptative apply LZ4HC on frame %d\n", frame); + if ((m_lz4Frames == 5 || m_lz4Frames == 6) && cSizeDelta > LZ4_ADAPTATIVE_CSIZE) + { + if (cSize <= cSizeDelta || m_lz4Frames == 5) + { + cSize = LZ4_compress_HC((char *)&m_pBufferBlit[field][0], m_pBufferLZ4[0], m_RGBSize, m_RGBSize, LZ4HC_CLEVEL_DEFAULT); + } + else + { + cSizeDelta = LZ4_compress_HC((char *)&m_pBufferBlitDelta[0], m_pBufferLZ4[1], m_RGBSize, m_RGBSize, LZ4HC_CLEVEL_DEFAULT); + ratio_delta = (double) cSizeDelta / cSize; + } + m_lz4Frames = m_lz4Frames - 2; + LOG(0,"[MiSTer] LZ4 Adaptative apply LZ4HC on frame %d\n", frame); + } } m_bufferSend[0] = CMD_BLIT_FIELD_VSYNC; @@ -485,16 +617,35 @@ void GroovyMister::CmdBlit(uint32_t frame, uint8_t field, uint16_t vCountSync, u memcpy(&m_bufferSend[6], &vSync, sizeof(vSync)); if (cSize > 0) { - memcpy(&m_bufferSend[8], &cSize, sizeof(cSize)); - bytesToSend = cSize; - Send(&m_bufferSend[0], 12); + if (ratio_delta < 0.95) + { + memcpy(&m_bufferSend[8], &cSizeDelta, sizeof(cSizeDelta)); + m_bufferSend[12] = 0x01; //frame_delta + bytesToSend = cSizeDelta; + Send(&m_bufferSend[0], 13); + } + else + { + memcpy(&m_bufferSend[8], &cSize, sizeof(cSize)); + bytesToSend = cSize; + Send(&m_bufferSend[0], 12); + } } else { - bytesToSend = m_RGBSize; - Send(&m_bufferSend[0], 8); + if (m_delta_enabled[field] && matchDeltaBytes == m_RGBSize) + { + m_bufferSend[8] = 0x01; //frame_dup + Send(&m_bufferSend[0], 9); + return; + } + else + { + bytesToSend = m_RGBSize; + Send(&m_bufferSend[0], 8); + } } - + if (m_doCongestionControl) { m_tickStart = m_tickCongestion; @@ -506,19 +657,21 @@ void GroovyMister::CmdBlit(uint32_t frame, uint8_t field, uint16_t vCountSync, u m_streamTime = DiffTime(); } } - + setTimeStart(); - SendStream(0, bytesToSend, cSize); + uint8_t buffer_blit = (cSize > 0) ? (ratio_delta < 0.95) ? 1 : 0 : field; + SendStream(0, buffer_blit, bytesToSend, (ratio_delta < 0.95) ? cSizeDelta : cSize); setTimeEnd(); m_streamTime = DiffTime(); m_tickCongestion = m_tickEnd; m_doCongestionControl = (bytesToSend >= K_CONGESTION_SIZE) ? 1 : 0; + m_delta_enabled[field] = 1; //printf("[DEBUG] Stream time , frame %d -> %lu\n",m_frame, m_streamTime); } void GroovyMister::CmdAudio(uint16_t soundSize) { - if (!fpga.audio) + if (!fpga.audio || !m_isConnected) { return; } @@ -527,11 +680,11 @@ void GroovyMister::CmdAudio(uint16_t soundSize) memcpy(&m_bufferSend[1], &soundSize, sizeof(soundSize)); Send(&m_bufferSend[0], 3); - SendStream(1, soundSize, 0); + SendStream(1, 0, soundSize, 0); } uint32_t GroovyMister::getACK(DWORD dwMilliseconds) -{ +{ uint32_t getACKresult = 0; uint32_t frameUDP = fpga.frameEcho; if (dwMilliseconds > 0) @@ -558,21 +711,33 @@ uint32_t GroovyMister::getACK(DWORD dwMilliseconds) if (results[idx].BytesTransferred == 13) //blit ACK { if (dwMilliseconds > 0) - { - setTimeEnd(); + { + setTimeEnd(); getACKresult = DiffTime(); - } - else - { - getACKresult = 1; - } - + } + else + { + getACKresult = 1; + } memcpy(&frameUDP, &m_bufferReceive[0], 4); - if (frameUDP > fpga.frameEcho) - { + if (frameUDP > fpga.frameEcho) + { setFpgaStatus(); } } + if (results[idx].BytesTransferred == 1) //getVersion + { + if (dwMilliseconds > 0) + { + setTimeEnd(); + getACKresult = DiffTime(); + } + else + { + getACKresult = 1; + } + memcpy(&m_core_version, &m_bufferReceive[0], 1); + } idx++; } while (idx <= numResults); numResults = m_rio.RIODequeueCompletion(m_receiveQueue, results, numResults); @@ -604,12 +769,20 @@ uint32_t GroovyMister::getACK(DWORD dwMilliseconds) setFpgaStatus(); } } + if (len == 1) //get version + { + getACKresult = diff; + memcpy(&m_core_version, &m_bufferReceive[0], 1); + } } while ((len > 0) || (!getACKresult && dwNanoseconds > diff)); return getACKresult; } void GroovyMister::WaitSync(void) { + if (!m_isConnected) + return; + m_tickStart = m_tickSync; setTimeEnd(); m_emulationTime = DiffTime(); @@ -700,7 +873,7 @@ void GroovyMister::BindInputs(const char* misterHost, uint16_t misterPort) m_sockInputsFD = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if (m_sockInputsFD < 0) { - LOG(0,"[MiSTer][Inputs] Could not create socket : %d", sockfd); + LOG(0,"[MiSTer][Inputs] Could not create socket : %d", m_sockInputsFD); } LOG(0,"[MiSTer][Input] Setting socket async %s...\n",""); // Non blocking socket @@ -819,20 +992,20 @@ if (USE_RIO) sendto(m_sockFD, (char *) cmd, cmdSize, 0, (struct sockaddr *)&m_serverAddr, sizeof(m_serverAddr)); } -void GroovyMister::SendStream(uint8_t whichBuffer, uint32_t bytesToSend, uint32_t cSize) -{ - DWORD flags = RIO_MSG_DONT_NOTIFY | RIO_MSG_DEFER; +void GroovyMister::SendStream(uint8_t whichBuffer, uint8_t field, uint32_t bytesToSend, uint32_t cSize) +{ uint32_t bytesSended = 0; #ifdef _WIN32 if (USE_RIO) { + DWORD flags = RIO_MSG_DONT_NOTIFY | RIO_MSG_DEFER; int i=0; while (bytesSended < bytesToSend) { if (whichBuffer == 0) { - m_pBufsBlit[i].Length = (bytesToSend - bytesSended >= m_mtu) ? m_mtu : bytesToSend - bytesSended; - m_rio.RIOSend(m_requestQueue, &m_pBufsBlit[i], 1, flags, &m_pBufsBlit[i]); + m_pBufsBlit[field][i].Length = (bytesToSend - bytesSended >= m_mtu) ? m_mtu : bytesToSend - bytesSended; + m_rio.RIOSend(m_requestQueue, &m_pBufsBlit[field][i], 1, flags, &m_pBufsBlit[field][i]); } else { @@ -853,16 +1026,16 @@ if (USE_RIO) { if (cSize > 0) { - Send(&m_pBufferLZ4[bytesSended], chunkSize); + Send(&m_pBufferLZ4[field][bytesSended], chunkSize); } else { - Send(&pBufferBlit[bytesSended], chunkSize); + Send(&m_pBufferBlit[field][bytesSended], chunkSize); } } else { - Send(&pBufferAudio[bytesSended], chunkSize); + Send(&m_pBufferAudio[bytesSended], chunkSize); } bytesSended += m_mtu; } diff --git a/deps/mister/groovymister.h b/deps/mister/groovymister.h index 4080b31f1000..c12762ccab2b 100644 --- a/deps/mister/groovymister.h +++ b/deps/mister/groovymister.h @@ -8,6 +8,12 @@ #include #include #include "rio.h" +#else + #include + #include + #include + #include + #include #endif #ifndef GROOVYMISTER_VERSION @@ -84,24 +90,26 @@ typedef unsigned long DWORD; class GroovyMister { public: - - char *pBufferBlit; // This buffer are registered and aligned for sending rgb. Populate it before CmdBlit - char *pBufferAudio; // This buffer are registered and aligned for sending audio. Populate it before CmdAudio + fpgaStatus fpga; // Data with last received ACK fpgaJoyInputs joyInputs; // Data with last joystick inputs received fpgaPS2Inputs ps2Inputs; // Data with last ps2 inputs received GroovyMister(); ~GroovyMister(); - + + char* getPBufferBlit(uint8_t field); // This buffer are registered and aligned for sending rgb. Populate it before CmdBlit + char* getPBufferBlitDelta(void); // This buffer are registered and aligned for sending rgb. Populate it before CmdBlit with delta difference between actual frame and last + char* getPBufferAudio(void); // This buffer are registered and aligned for sending audio. Populate it before CmdAudio + // Close connection void CmdClose(void); - // Init streaming with ip, port, (lz4frames = 0-raw, 1-lz4, 2-lz4hc, 3-lz4 adaptative), soundRate(1-22k, 2-44.1, 3-48 khz), soundChan(1 or 2), rgbMode(0-RGB888, 1-RGBA888, 2-RGB565), mtu source - uint8_t CmdInit(const char* misterHost, uint16_t misterPort, uint8_t lz4Frames, uint32_t soundRate, uint8_t soundChan, uint8_t rgbMode, uint16_t mtu); + // Init streaming with ip, port + int CmdInit(const char* misterHost, uint16_t misterPort, int lz4Frames, uint32_t soundRate, uint8_t soundChan, uint8_t rgbMode, uint16_t mtu); // Change resolution (check https://github.com/antonioginer/switchres) with modeline void CmdSwitchres(double pClock, uint16_t hActive, uint16_t hBegin, uint16_t hEnd, uint16_t hTotal, uint16_t vActive, uint16_t vBegin, uint16_t vEnd, uint16_t vTotal, uint8_t interlace); // Stream frame, field = 0 for progressive, vCountSync = 0 for auto frame delay or number of vertical line to sync with, margin with nanoseconds for auto frame delay) - void CmdBlit(uint32_t frame, uint8_t field, uint16_t vCountSync, uint32_t margin); + void CmdBlit(uint32_t frame, uint8_t field, uint16_t vCountSync, uint32_t margin, uint32_t matchDeltaBytes); // Stream audio void CmdAudio(uint16_t soundSize); // getACK is used internal on WaitSync, dwMilliseconds = 0 will time out immediately if no new data @@ -132,9 +140,8 @@ class GroovyMister RIO_BUF m_sendRioBuffer; RIO_BUFFERID m_receiveRioBufferId; RIO_BUF m_receiveRioBuffer; - RIO_BUFFERID m_sendRioBufferBlitId; - RIO_BUF m_sendRioBufferBlit; - RIO_BUF *m_pBufsBlit; + RIO_BUFFERID m_sendRioBufferBlitId[2]; + RIO_BUF *m_pBufsBlit[2]; RIO_BUFFERID m_sendRioBufferAudioId; RIO_BUF m_sendRioBufferAudio; RIO_BUF *m_pBufsAudio; @@ -158,8 +165,10 @@ class GroovyMister char m_bufferSend[26]; char m_bufferReceive[13]; char m_bufferInputsReceive[41]; - char *m_pBufferLZ4; + char *m_pBufferBlit[2]; + char *m_pBufferBlitDelta; char *m_pBufferAudio; + char *m_pBufferLZ4[2]; uint8_t m_lz4Frames; uint8_t m_soundChan; uint8_t m_rgbMode; @@ -173,10 +182,14 @@ class GroovyMister uint32_t m_emulationTime; uint16_t m_mtu; uint8_t m_doCongestionControl; + uint8_t m_core_version; + uint32_t m_network_ping; + uint8_t m_delta_enabled[2]; + uint8_t m_isConnected; char *AllocateBufferSpace(const DWORD bufSize, const DWORD bufCount, DWORD& totalBufferSize, DWORD& totalBufferCount); void Send(void *cmd, int cmdSize); - void SendStream(uint8_t whichBuffer, uint32_t bytesToSend, uint32_t cSize); + void SendStream(uint8_t whichBuffer, uint8_t field, uint32_t bytesToSend, uint32_t cSize); void setTimeStart(void); void setTimeEnd(void); uint32_t DiffTime(void); diff --git a/deps/mister/groovymister_wrapper.cpp b/deps/mister/groovymister_wrapper.cpp index f9f11cba9d37..71398bbe5c44 100644 --- a/deps/mister/groovymister_wrapper.cpp +++ b/deps/mister/groovymister_wrapper.cpp @@ -19,14 +19,14 @@ extern "C" { GroovyMister* gmw; int gmw_inputsBinded; -MODULE_API_GMW void gmw_init(const char* misterHost, uint8_t lz4Frames, uint32_t soundRate, uint8_t soundChan, uint8_t rgbMode, uint16_t mtu) +MODULE_API_GMW int gmw_init(const char* misterHost, uint8_t lz4Frames, uint32_t soundRate, uint8_t soundChan, uint8_t rgbMode, uint16_t mtu) { if (gmw == NULL) { gmw = new GroovyMister; gmw_inputsBinded = 0; } - gmw->CmdInit(misterHost, 32100, lz4Frames, soundRate, soundChan, rgbMode, mtu); + return gmw->CmdInit(misterHost, 32100, lz4Frames, soundRate, soundChan, rgbMode, mtu); } MODULE_API_GMW void gmw_close(void) @@ -56,11 +56,11 @@ MODULE_API_GMW void gmw_switchres(double pClock, uint16_t hActive, uint16_t hBeg } } -MODULE_API_GMW char* gmw_get_pBufferBlit(void) +MODULE_API_GMW char* gmw_get_pBufferBlit(uint8_t field) { if (gmw != NULL) { - return gmw->pBufferBlit; + return gmw->getPBufferBlit(field); } else { @@ -69,11 +69,24 @@ MODULE_API_GMW char* gmw_get_pBufferBlit(void) } } -MODULE_API_GMW void gmw_blit(uint32_t frame, uint8_t field, uint16_t vCountSync, uint32_t margin) +MODULE_API_GMW char* gmw_get_pBufferBlitDelta(void) { if (gmw != NULL) { - gmw->CmdBlit(frame, field, vCountSync, margin); + return gmw->getPBufferBlitDelta(); + } + else + { + printf("[MiSTer] gmw_get_pBufferBlitDelta failed\n"); + return NULL; + } +} + +MODULE_API_GMW void gmw_blit(uint32_t frame, uint8_t field, uint16_t vCountSync, uint32_t margin, uint32_t matchDeltaBytes) +{ + if (gmw != NULL) + { + gmw->CmdBlit(frame, field, vCountSync, margin, matchDeltaBytes); } else { @@ -85,7 +98,7 @@ MODULE_API_GMW char* gmw_get_pBufferAudio(void) { if (gmw != NULL) { - return gmw->pBufferAudio; + return gmw->getPBufferAudio(); } else { diff --git a/deps/mister/groovymister_wrapper.h b/deps/mister/groovymister_wrapper.h index 5676d5c85f67..0db28369c867 100644 --- a/deps/mister/groovymister_wrapper.h +++ b/deps/mister/groovymister_wrapper.h @@ -108,18 +108,50 @@ typedef struct MODULE_API_GMW{ uint8_t ps2MouseZ; //byte 3 ps2 mouse Z } gmw_fpgaPS2Inputs; +enum Lz4FramesCode { //gmw_init lz4Frames + LZ4_OFF = 0, //RAW frames + LZ4 = 1, + LZ4_DELTA = 2, + LZ4_HC = 3, + LZ4_HC_DELTA = 4, + LZ4_ADPTATIVE = 5, + LZ4_ADPTATIVE_DELTA = 6 +}; + +enum SoundRateCode { //gmw_init soundRate + RATE_OFF = 0, + RATE_22050 = 1, + RATE_44100 = 2, + RATE_48000 = 3 +}; + +enum SoundChanCode { //gmw_init soundChan + CHAN_OFF = 0, + CHAN_MONO = 1, + CHAN_STEREO = 2 +}; + +enum RGBModeCode { //gmw_init rgbMode + RGB_888 = 0, + RGB_A888 = 1, + RGB_565 = 2 +}; + /* Declaration of the wrapper functions */ -// Init streaming with ip, port, (lz4frames = 0-raw, 1-lz4, 2-lz4hc, 3-lz4 adaptative), soundRate(1-22k, 2-44.1, 3-48 khz), soundChan(1 or 2), rgbMode(0-RGB888, 1-RGBA888, 2-RGB565), mtu source -MODULE_API_GMW void gmw_init(const char* misterHost, uint8_t lz4Frames, uint32_t soundRate, uint8_t soundChan, uint8_t rgbMode, uint16_t mtu); +// Init streaming with ip, port and mtu size (typical 1500 or 3800 for MiSTer jumbo frames) +// A negative value is returned if connection fails +MODULE_API_GMW int gmw_init(const char* misterHost, uint8_t lz4Frames, uint32_t soundRate, uint8_t soundChan, uint8_t rgbMode, uint16_t mtu); // Close stream MODULE_API_GMW void gmw_close(void); // Change resolution (check https://github.com/antonioginer/switchres) for modeline generation (interlace=2 for progressive framebuffer) MODULE_API_GMW void gmw_switchres(double pClock, uint16_t hActive, uint16_t hBegin, uint16_t hEnd, uint16_t hTotal, uint16_t vActive, uint16_t vBegin, uint16_t vEnd, uint16_t vTotal, uint8_t interlace); // This buffer are registered and aligned for sending rgb. Populate it before gmw_blit -MODULE_API_GMW char* gmw_get_pBufferBlit(void); -// Stream frame, field = 0 for progressive, vCountSync = 0 for auto frame delay or number of vertical line to sync with, margin with nanoseconds for auto frame delay) -MODULE_API_GMW void gmw_blit(uint32_t frame, uint8_t field, uint16_t vCountSync, uint32_t margin); +MODULE_API_GMW char* gmw_get_pBufferBlit(uint8_t field); +// This buffer are registered and aligned for sending rgb. Populate it before gmw_blit. Here will be difference between actual frame and last with 8-bit overflow +MODULE_API_GMW char* gmw_get_pBufferBlitDelta(void); +// Stream frame, field = 0 for progressive, vCountSync = 0 for auto frame delay or number of vertical line to sync with, margin with nanoseconds for auto frame delay), matchDeltaBytes for delta frames +MODULE_API_GMW void gmw_blit(uint32_t frame, uint8_t field, uint16_t vCountSync, uint32_t margin, uint32_t matchDeltaBytes); // This buffer are registered and aligned for sending rgb. Populate it before gmw_audio MODULE_API_GMW char* gmw_get_pBufferAudio(void); // Stream audio @@ -142,7 +174,7 @@ MODULE_API_GMW void gmw_getJoyInputs(gmw_fpgaJoyInputs* joyInputs); MODULE_API_GMW void gmw_getPS2Inputs(gmw_fpgaPS2Inputs* ps2Inputs); // get version -MODULE_API_GMW const char* gmw_get_version(); +MODULE_API_GMW const char* gmw_get_version(void); // Verbose level 0,1,2 (min to max) MODULE_API_GMW void gmw_set_log_level(int level); @@ -150,11 +182,12 @@ MODULE_API_GMW void gmw_set_log_level(int level); /* Inspired by https://stackoverflow.com/a/1067684 */ typedef struct MODULE_API_GMW { - void (*init)(const char* misterHost, uint8_t lz4Frames, uint32_t soundRate, uint8_t soundChan, uint8_t rgbMode, uint16_t mtu); + int (*init)(const char* misterHost, uint8_t lz4Frames, uint32_t soundRate, uint8_t soundChan, uint8_t rgbMode, uint16_t mtu); void (*close)(void); void (*switchres)(double pClock, uint16_t hActive, uint16_t hBegin, uint16_t hEnd, uint16_t hTotal, uint16_t vActive, uint16_t vBegin, uint16_t vEnd, uint16_t vTotal, uint8_t interlace); - char*(*get_pBufferBlit)(void); - void (*blit)(uint32_t frame, uint8_t field, uint16_t vCountSync, uint32_t margin); + char*(*get_pBufferBlit)(uint8_t field); + char*(*get_pBufferBlitDelta)(void); + void (*blit)(uint32_t frame, uint8_t field, uint16_t vCountSync, uint32_t margin, uint32_t matchDeltaBytes); char*(*get_pBufferAudio)(void); void (*audio)(uint16_t soundSize); void (*waitSync)(void); diff --git a/gfx/gfx_mister.c b/gfx/gfx_mister.c index 8872f6aa25bd..178db025b33e 100644 --- a/gfx/gfx_mister.c +++ b/gfx/gfx_mister.c @@ -21,6 +21,7 @@ typedef struct mister_video_info { + bool is_error; bool is_connected; uint32_t frame; uint8_t field; @@ -31,6 +32,7 @@ typedef struct mister_video_info uint32_t line_time; //usec uint32_t frame_time; //usec uint8_t rgb_mode; + bool delta_frames; } mister_video_t; union @@ -58,6 +60,7 @@ static bool vp_resize_pending = 0; static bool prev_menu_state = 0; static struct scaler_ctx *scaler; static uint8_t *mister_buffer = 0; +static uint8_t *mister_buffer_delta = 0; static uint8_t *audio_buffer = 0; static uint8_t *convert_buffer = 0; static uint8_t *scaled_buffer = 0; @@ -85,7 +88,9 @@ void mister_draw(video_driver_state_t *video_st, const void *data, unsigned widt bool menu_on = false; bool stretched = false; bool is_hw_rendered = (data == RETRO_HW_FRAME_BUFFER_VALID - && video_st->frame_cache_data == RETRO_HW_FRAME_BUFFER_VALID); + && video_st->frame_cache_data == RETRO_HW_FRAME_BUFFER_VALID) + || (settings->bools.mister_force_scaler + && !(menu_state_get_ptr()->flags & MENU_ST_FLAG_ALIVE)); // Initialize MiSTer if required if (!mister_video.is_connected) @@ -268,8 +273,12 @@ void mister_draw(video_driver_state_t *video_st, const void *data, unsigned widt u.u8 += (field + (y_crop / 2)) * pitch + (x_crop / 2); // Get target frame buffer + mister_buffer = (uint8_t*)gmw_get_pBufferBlit(field); uint8_t *fb = mister_video.rgb_mode == RGB565 && format != SCALER_FMT_RGB565 ? convert_buffer : mister_buffer; - + + if ((mister_video.rgb_mode == RGB565 && format != SCALER_FMT_RGB565)) + mister_video.delta_frames = false; + // Compute steps to walk through the source & target bitmaps uint32_t pix_size = (mister_video.rgb_mode == RGB565 && format == SCALER_FMT_RGB565) ? 2 : 3; uint32_t c = 0; @@ -300,7 +309,8 @@ void mister_draw(video_driver_state_t *video_st, const void *data, unsigned widt s_step = mister_video.interlaced ? 2 : 1; break; } - + + uint32_t match_delta = 0; // Copy RGB buffer as BGR24 for (uint32_t j = 0; j < y_max - 1; j++) { @@ -319,7 +329,14 @@ void mister_draw(video_driver_state_t *video_st, const void *data, unsigned widt for (uint32_t i = 0; i < x_max - 1; i += s_step) { if (scanlines && (j % 2)) - { + { + if (mister_video.delta_frames) + { + match_delta += 3; + mister_buffer_delta[c + 0] = 0; //b + mister_buffer_delta[c + 1] = 0; //g + mister_buffer_delta[c + 2] = 0; //r + } fb[c + 0] = 0; //b fb[c + 1] = 0; //g fb[c + 2] = 0; //r @@ -327,6 +344,39 @@ void mister_draw(video_driver_state_t *video_st, const void *data, unsigned widt else if (format == SCALER_FMT_RGBA4444) { uint16_t pixel = u.u16[i]; + if (mister_video.delta_frames) + { + if ((pixel >> 8) == fb[c + 0]) + { + match_delta++; + mister_buffer_delta[c + 0] = 0; + } + else + { + mister_buffer_delta[c + 0] = (uint8_t) (pixel >> 8) - (uint8_t) fb[c + 0]; + } + if ((pixel >> 4) == fb[c + 1]) + { + match_delta++; + mister_buffer_delta[c + 1] = 0; + } + else + { + mister_buffer_delta[c + 1] = (uint8_t) (pixel >> 4) - (uint8_t) fb[c + 1]; + } + if ((pixel >> 0) == fb[c + 2]) + { + match_delta++; + mister_buffer_delta[c + 2] = 0; + } + else + { + mister_buffer_delta[c + 2] = (uint8_t) (pixel >> 0) - (uint8_t) fb[c + 2]; + } + } + mister_buffer_delta[c + 0] = 0; //b + mister_buffer_delta[c + 1] = 0; //g + mister_buffer_delta[c + 2] = 0; //r fb[c + 0] = (pixel >> 8); //b fb[c + 1] = (pixel >> 4); //g fb[c + 2] = (pixel >> 0); //r @@ -334,6 +384,36 @@ void mister_draw(video_driver_state_t *video_st, const void *data, unsigned widt else if (format == SCALER_FMT_BGR24) { uint32_t pixel = i * pix_size; + if (mister_video.delta_frames) + { + if (u.u8[pixel + 0] == fb[c + 0]) + { + match_delta++; + mister_buffer_delta[c + 0] = 0; + } + else + { + mister_buffer_delta[c + 0] = (uint8_t) u.u8[pixel + 0] - (uint8_t) fb[c + 0]; + } + if (u.u8[pixel + 1] == fb[c + 1]) + { + match_delta++; + mister_buffer_delta[c + 1] = 0; + } + else + { + mister_buffer_delta[c + 1] = (uint8_t) u.u8[pixel + 1] - (uint8_t) fb[c + 1]; + } + if (u.u8[pixel + 2] == fb[c + 2]) + { + match_delta++; + mister_buffer_delta[c + 2] = 0; + } + else + { + mister_buffer_delta[c + 2] = (uint8_t) u.u8[pixel + 2] - (uint8_t) fb[c + 2]; + } + } fb[c + 0] = u.u8[pixel + 0]; //b fb[c + 1] = u.u8[pixel + 1]; //g fb[c + 2] = u.u8[pixel + 2]; //r @@ -343,7 +423,28 @@ void mister_draw(video_driver_state_t *video_st, const void *data, unsigned widt uint16_t pixel = u.u16[i]; if (mister_video.rgb_mode == RGB565) { - uint16_t *target = (uint16_t *)&fb[c]; + uint16_t *target = (uint16_t *)&fb[c]; + if (mister_video.delta_frames) + { + if (((pixel >> 0) & 0xff) == fb[c + 0]) + { + match_delta++; + mister_buffer_delta[c + 0] = 0; + } + else + { + mister_buffer_delta[c + 0] = (uint8_t) ((pixel >> 0) & 0xff) - (uint8_t) fb[c + 0]; + } + if (((pixel >> 8) & 0xff) == fb[c + 1]) + { + match_delta++; + mister_buffer_delta[c + 1] = 0; + } + else + { + mister_buffer_delta[c + 1] = (uint8_t) ((pixel >> 8) & 0xff) - (uint8_t) fb[c + 1]; + } + } *target = pixel; } else @@ -351,6 +452,36 @@ void mister_draw(video_driver_state_t *video_st, const void *data, unsigned widt uint8_t r = (pixel >> 11) & 0x1f; uint8_t g = (pixel >> 5) & 0x3f; uint8_t b = (pixel >> 0) & 0x1f; + if (mister_video.delta_frames) + { + if (((b << 3) | (b >> 2)) == fb[c + 0]) + { + match_delta++; + mister_buffer_delta[c + 0] = 0; + } + else + { + mister_buffer_delta[c + 0] = (uint8_t) ((b << 3) | (b >> 2)) - (uint8_t) fb[c + 0]; + } + if (((g << 2) | (g >> 4)) == fb[c + 1]) + { + match_delta++; + mister_buffer_delta[c + 1] = 0; + } + else + { + mister_buffer_delta[c + 1] = (uint8_t) ((g << 2) | (g >> 4)) - (uint8_t) fb[c + 1]; + } + if (((r << 3) | (r >> 2)) == fb[c + 2]) + { + match_delta++; + mister_buffer_delta[c + 2] = 0; + } + else + { + mister_buffer_delta[c + 2] = (uint8_t) ((r << 3) | (r >> 2)) - (uint8_t) fb[c + 2]; + } + } fb[c + 0] = (b << 3) | (b >> 2); //b fb[c + 1] = (g << 2) | (g >> 4); //g fb[c + 2] = (r << 3) | (r >> 2); //r @@ -359,6 +490,36 @@ void mister_draw(video_driver_state_t *video_st, const void *data, unsigned widt else if (format == SCALER_FMT_ARGB8888) { uint32_t pixel = u.u32[i]; + if (mister_video.delta_frames) + { + if (((pixel >> 0) & 0xff) == fb[c + 0]) + { + match_delta++; + mister_buffer_delta[c + 0] = 0; + } + else + { + mister_buffer_delta[c + 0] = (uint8_t) ((pixel >> 0) & 0xff) - (uint8_t) fb[c + 0]; + } + if (((pixel >> 8) & 0xff) == fb[c + 1]) + { + match_delta++; + mister_buffer_delta[c + 1] = 0; + } + else + { + mister_buffer_delta[c + 1] = (uint8_t) ((pixel >> 8) & 0xff) - (uint8_t) fb[c + 1]; + } + if (((pixel >> 16) & 0xff) == fb[c + 2]) + { + match_delta++; + mister_buffer_delta[c + 2] = 0; + } + else + { + mister_buffer_delta[c + 2] = (uint8_t) ((pixel >> 16) & 0xff) - (uint8_t) fb[c + 2]; + } + } fb[c + 0] = (pixel >> 0) & 0xff; //b fb[c + 1] = (pixel >> 8) & 0xff; //g fb[c + 2] = (pixel >> 16) & 0xff; //r @@ -391,12 +552,15 @@ void mister_draw(video_driver_state_t *video_st, const void *data, unsigned widt mister_video.frame = status.frame + 1; // Blit to MiSTer - gmw_blit(mister_video.frame, mister_video.field, mister_vsync, 0); + gmw_blit(mister_video.frame, mister_video.field, mister_vsync, 0, match_delta); } static void mister_init(const char* mister_host, uint8_t compression, uint32_t sound_rate, uint8_t sound_channels, uint8_t pix_fmt) { + if (mister_video.is_error) + return; + settings_t *settings = config_get_ptr(); mister_video.frame = 0; mister_video.width = 0; @@ -405,14 +569,23 @@ static void mister_init(const char* mister_host, uint8_t compression, uint32_t s mister_video.frame_time = 0; mister_video.interlaced = 0; mister_video.rgb_mode = (pix_fmt == RETRO_PIXEL_FORMAT_RGB565 || settings->bools.mister_force_rgb565) ? RGB565 : RGB888; + mister_video.delta_frames = (compression % 2 == 0) ? true : false; RARCH_LOG("[MiSTer] Sending CMD_INIT... lz4 %d sound_rate %d sound_chan %d rgb_mode %d mtu %d\n", compression, sound_rate, sound_channels, mister_video.rgb_mode, settings->uints.mister_mtu); - gmw_init(mister_host, compression, sound_rate, sound_channels, mister_video.rgb_mode, settings->uints.mister_mtu); - - mister_video.is_connected = true; + if (gmw_init(mister_host, compression, sound_rate, sound_channels, mister_video.rgb_mode, settings->uints.mister_mtu) < 0) + { + mister_video.is_error = true; + mister_video.is_connected = false; + RARCH_LOG("[MiSTer] CMD_INIT... failed\n"); + } + else + { + mister_video.is_connected = true; + } // Allocate buffers - mister_buffer = (uint8_t*)gmw_get_pBufferBlit(); + mister_buffer = (uint8_t*)gmw_get_pBufferBlit(0); + mister_buffer_delta = (uint8_t*)gmw_get_pBufferBlitDelta(); audio_buffer = (uint8_t*)gmw_get_pBufferAudio(); convert_buffer = (uint8_t*)malloc(MAX_BUFFER_WIDTH * MAX_BUFFER_HEIGHT * 4); scaled_buffer = (uint8_t*)malloc(MAX_BUFFER_WIDTH * MAX_BUFFER_HEIGHT * 4);