Changeset 23399 in vbox for trunk/src/VBox
- Timestamp:
- Sep 29, 2009 5:04:38 AM (15 years ago)
- svn:sync-xref-src-repo-rev:
- 52936
- Location:
- trunk/src/VBox
- Files:
-
- 1 added
- 17 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Additions/common/crOpenGL/Makefile.kmk
r22872 r23399 377 377 pack/packspu_texture.c \ 378 378 pack/packspu_getshaders.c \ 379 pack/packspu_glsl.c \ 379 380 $(VBOX_PATH_CROGL_GENFILES)/packspu.c \ 380 381 $(VBOX_PATH_CROGL_GENFILES)/packspu_get.c \ -
trunk/src/VBox/Additions/common/crOpenGL/getprocaddress.py
r21022 r23399 56 56 for func_name in keys: 57 57 if (func_name == "Writeback" or 58 func_name == "BoundsInfoCR"): 58 func_name == "BoundsInfoCR" or 59 func_name == "GetUniformsLocations"): 59 60 continue 60 61 if apiutil.Category(func_name) == "Chromium": -
trunk/src/VBox/Additions/common/crOpenGL/pack/packspu_misc.c
r21127 r23399 231 231 return return_val; 232 232 } 233 234 void PACKSPU_APIENTRY 235 packspu_BindFramebufferEXT(GLenum target, GLuint framebuffer) 236 { 237 crStateBindFramebufferEXT(target, framebuffer); 238 crPackBindFramebufferEXT(target, framebuffer); 239 } -
trunk/src/VBox/Additions/common/crOpenGL/pack/packspu_special
r22284 r23399 80 80 LockArraysEXT 81 81 UnlockArraysEXT 82 CreateProgram 83 LinkProgram 84 DeleteProgram 85 GetUniformLocation 86 GetUniformsLocations 87 BindFramebufferEXT -
trunk/src/VBox/Additions/common/crOpenGL/windows_getprocaddress.py
r22011 r23399 61 61 for func_name in keys: 62 62 if (func_name == "Writeback" or 63 func_name == "BoundsInfoCR"): 63 func_name == "BoundsInfoCR" or 64 func_name == "GetUniformsLocations"): 64 65 continue 65 66 if apiutil.Category(func_name) == "Chromium": -
trunk/src/VBox/GuestHost/OpenGL/glapi_parser/APIspec.txt
r23274 r23399 10220 10220 chromium extpack 10221 10221 10222 # custom 10223 name GetUniformsLocations 10224 return void 10225 param program GLuint 10226 param maxcbData GLsizei 10227 param cbData GLsizei * 10228 param pData GLvoid * 10229 category Chromium 10230 props get 10231 chromium extpack 10232 10222 10233 # end of file sentinel 10223 10234 -
trunk/src/VBox/GuestHost/OpenGL/include/cr_protocol.h
r23274 r23399 16 16 /*For now guest is allowed to connect host opengl service if protocol version matches exactly*/ 17 17 /*Note: that after any change to this file, or glapi_parser\apispec.txt version should be changed*/ 18 #define CR_PROTOCOL_VERSION_MAJOR 418 #define CR_PROTOCOL_VERSION_MAJOR 5 19 19 #define CR_PROTOCOL_VERSION_MINOR 1 20 20 -
trunk/src/VBox/GuestHost/OpenGL/include/state/cr_glsl.h
r23274 r23399 64 64 GLenum type; 65 65 GLvoid *data; 66 #ifdef IN_GUEST 67 GLuint location; 68 #endif 66 69 } CRGLSLUniform; 67 70 … … 72 75 CRGLSLUniform *pUniforms; 73 76 GLuint cUniforms; 77 #ifdef IN_GUEST 78 GLboolean bUniformsSynced; /*uniforms info is updated since last link program call.*/ 79 #endif 74 80 } CRGLSLProgram; 75 81 … … 98 104 DECLEXPORT(void) STATE_APIENTRY crStateCreateProgram(GLuint id); 99 105 106 DECLEXPORT(GLboolean) STATE_APIENTRY crStateIsProgramUniformsCached(GLuint program); 107 108 #ifdef IN_GUEST 109 DECLEXPORT(void) STATE_APIENTRY crStateGLSLProgramCacheUniforms(GLuint program, GLsizei cbData, GLvoid *pData); 110 #else 111 DECLEXPORT(void) STATE_APIENTRY crStateGLSLProgramCacheUniforms(GLuint program, GLsizei maxcbData, GLsizei *cbData, GLvoid *pData); 112 #endif 113 100 114 #ifdef __cplusplus 101 115 } -
trunk/src/VBox/GuestHost/OpenGL/packer/pack_shaders.c
r20511 r23399 272 272 } 273 273 274 /*@todo next 7functions are bit hacky,274 /*@todo next 8 functions are bit hacky, 275 275 * we expect packspu to pass a single structure with all output parameters via first output pointer. 276 276 * it'd be better to add CRMessageMultiReadback one day. … … 408 408 } 409 409 410 void PACK_APIENTRY crPackGetUniformsLocations(GLuint program, GLsizei maxcbData, GLsizei * cbData, GLvoid * pData, int * writeback) 411 { 412 GET_PACKER_CONTEXT(pc); 413 unsigned char *data_ptr; 414 (void) pData; 415 GET_BUFFERED_POINTER(pc, 32); 416 WRITE_DATA(0, GLint, 32); 417 WRITE_DATA(4, GLenum, CR_GETUNIFORMSLOCATIONS_EXTEND_OPCODE); 418 WRITE_DATA(8, GLuint, program); 419 WRITE_DATA(12, GLsizei, maxcbData); 420 WRITE_NETWORK_POINTER(16, (void *) cbData); 421 WRITE_NETWORK_POINTER(24, (void *) writeback); 422 WRITE_OPCODE(pc, CR_EXTEND_OPCODE); 423 } 424 410 425 void PACK_APIENTRY crPackGetAttribLocation(GLuint program, const char * name, GLint * return_value, int * writeback) 411 426 { -
trunk/src/VBox/GuestHost/OpenGL/packer/packer_special
r22284 r23399 170 170 DeleteRenderbuffersEXT 171 171 LockArraysEXT 172 GetUniformsLocations -
trunk/src/VBox/GuestHost/OpenGL/state_tracker/state_glsl.c
r23276 r23399 73 73 } 74 74 75 static void crStateFreeProgramUniforms(CRGLSLProgram* pProgram) 76 { 77 GLuint i; 78 79 for (i=0; i<pProgram->cUniforms; ++i) 80 { 81 if (pProgram->pUniforms[i].name) crFree(pProgram->pUniforms[i].name); 82 if (pProgram->pUniforms[i].data) crFree(pProgram->pUniforms[i].data); 83 } 84 85 if (pProgram->pUniforms) crFree(pProgram->pUniforms); 86 87 pProgram->pUniforms = NULL; 88 pProgram->cUniforms = 0; 89 90 #ifdef IN_GUEST 91 pProgram->bUniformsSynced = GL_FALSE; 92 #endif 93 } 94 95 static void crStateShaderDecRefCount(void *data) 96 { 97 CRGLSLShader *pShader = (CRGLSLShader *) data; 98 99 CRASSERT(pShader->refCount>0); 100 101 pShader->refCount--; 102 103 if (0==pShader->refCount && pShader->deleted) 104 { 105 CRContext *g = GetCurrentContext(); 106 crHashtableDelete(g->glsl.shaders, pShader->id, crStateFreeGLSLShader); 107 } 108 } 109 110 static void crStateFakeDecRefCountCB(unsigned long key, void *data1, void *data2) 111 { 112 CRGLSLShader *pShader = (CRGLSLShader *) data1; 113 CRContext *ctx = (CRContext*) data2; 114 CRGLSLShader *pRealShader; 115 (void) key; 116 117 pRealShader = crStateGetShaderObj(pShader->id); 118 119 if (pRealShader) 120 { 121 crStateShaderDecRefCount(pRealShader); 122 } 123 else 124 { 125 crWarning("crStateFakeDecRefCountCB: NULL pRealShader"); 126 } 127 } 128 75 129 static void crStateFreeGLSLProgram(void *data) 76 130 { 77 131 CRGLSLProgram* pProgram = (CRGLSLProgram *) data; 78 132 133 crFreeHashtable(pProgram->currentState.attachedShaders, crStateShaderDecRefCount); 134 79 135 if (pProgram->activeState.attachedShaders) 80 136 { 137 CRContext *g = GetCurrentContext(); 138 crHashtableWalk(pProgram->activeState.attachedShaders, crStateFakeDecRefCountCB, g); 81 139 crFreeHashtable(pProgram->activeState.attachedShaders, crStateFreeGLSLShader); 82 140 } 83 141 84 crFreeHashtable(pProgram->currentState.attachedShaders, NULL);85 86 142 crStateFreeProgramAttribs(pProgram); 87 143 88 if (pProgram->pUniforms) crFree(pProgram->pUniforms);144 crStateFreeProgramUniforms(pProgram); 89 145 90 146 crFree(pProgram); … … 107 163 DECLEXPORT(void) STATE_APIENTRY crStateGLSLDestroy(CRContext *ctx) 108 164 { 165 CRContext *g = GetCurrentContext(); 166 167 /*@todo: hack to allow crStateFreeGLSLProgram to work correctly, 168 as the current context isn't the one being destroyed*/ 169 #ifdef CHROMIUM_THREADSAFE 170 crSetTSD(&__contextTSD, ctx); 171 #else 172 __currentContext = ctx; 173 #endif 174 109 175 crFreeHashtable(ctx->glsl.programs, crStateFreeGLSLProgram); 110 176 crFreeHashtable(ctx->glsl.shaders, crStateFreeGLSLShader); 177 178 #ifdef CHROMIUM_THREADSAFE 179 crSetTSD(&__contextTSD, g); 180 #else 181 __currentContext = g; 182 #endif 183 111 184 } 112 185 … … 225 298 pProgram->pUniforms = NULL; 226 299 pProgram->cUniforms = 0; 300 #ifdef IN_GUEST 301 pProgram->bUniformsSynced = GL_FALSE; 302 #endif 227 303 228 304 crHashtableAdd(g->glsl.programs, id, pProgram); … … 341 417 } 342 418 343 static void crStateShaderDecRefCount(void *data)344 {345 CRGLSLShader *pShader = (CRGLSLShader *) data;346 347 CRASSERT(pShader->refCount>0);348 349 pShader->refCount--;350 351 if (0==pShader->refCount && pShader->deleted)352 {353 CRContext *g = GetCurrentContext();354 crHashtableDelete(g->glsl.shaders, pShader->id, crStateFreeGLSLShader);355 }356 }357 358 static void crStateFakeDecRefCountCB(unsigned long key, void *data1, void *data2)359 {360 CRGLSLShader *pShader = (CRGLSLShader *) data1;361 CRContext *ctx = (CRContext*) data2;362 CRGLSLShader *pRealShader;363 (void) key;364 365 pRealShader = crStateGetShaderObj(pShader->id);366 CRASSERT(pRealShader);367 crStateShaderDecRefCount(pRealShader);368 }369 370 419 DECLEXPORT(void) STATE_APIENTRY crStateDeleteProgram(GLuint program) 371 420 { … … 382 431 { 383 432 g->glsl.activeProgram = NULL; 384 }385 386 crFreeHashtable(pProgram->currentState.attachedShaders, crStateShaderDecRefCount);387 388 if (pProgram->activeState.attachedShaders)389 {390 crHashtableWalk(pProgram->activeState.attachedShaders, crStateFakeDecRefCountCB, g);391 433 } 392 434 … … 414 456 GLint sLen=0; 415 457 458 CRASSERT(pRealShader); 416 459 pRealShader->refCount++; 417 460 … … 472 515 crHashtableWalk(pProgram->currentState.attachedShaders, crStateCopyShaderCB, pProgram); 473 516 517 /*that's not a bug, note the memcpy above*/ 474 518 if (pProgram->activeState.pAttribs) 475 519 { … … 482 526 pProgram->activeState.pAttribs[i].name = crStrdup(pProgram->currentState.pAttribs[i].name); 483 527 } 528 529 crStateFreeProgramUniforms(pProgram); 484 530 } 485 531 … … 519 565 } 520 566 521 crMemcpy(&pAttribs[0], &pProgram->currentState.pAttribs[0], pProgram->currentState.cAttribs*sizeof(CRGLSLAttrib)); 567 if (pProgram->currentState.cAttribs) 568 { 569 crMemcpy(&pAttribs[0], &pProgram->currentState.pAttribs[0], pProgram->currentState.cAttribs*sizeof(CRGLSLAttrib)); 570 } 522 571 pAttribs[pProgram->currentState.cAttribs].index = index; 523 572 pAttribs[pProgram->currentState.cAttribs].name = crStrdup(name); … … 616 665 } 617 666 667 DECLEXPORT(GLboolean) STATE_APIENTRY crStateIsProgramUniformsCached(GLuint program) 668 { 669 CRGLSLProgram *pProgram = crStateGetProgramObj(program); 670 671 if (!pProgram) 672 { 673 crWarning("Unknown program %d", program); 674 return GL_FALSE; 675 } 676 677 #ifdef IN_GUEST 678 return pProgram->bUniformsSynced; 679 #else 680 crWarning("crStateIsProgramUniformsCached called on host side!!"); 681 return GL_FALSE; 682 #endif 683 } 684 685 /*@todo: one of those functions should ignore uniforms starting with "gl"*/ 686 687 #ifdef IN_GUEST 688 DECLEXPORT(void) STATE_APIENTRY 689 crStateGLSLProgramCacheUniforms(GLuint program, GLsizei cbData, GLvoid *pData) 690 { 691 CRGLSLProgram *pProgram = crStateGetProgramObj(program); 692 char *pCurrent = pData; 693 GLsizei cbRead, cbName; 694 GLuint i; 695 696 if (!pProgram) 697 { 698 crWarning("Unknown program %d", program); 699 return; 700 } 701 702 if (pProgram->bUniformsSynced) 703 { 704 crWarning("crStateGLSLProgramCacheUniforms: this shouldn't happen!"); 705 crStateFreeProgramUniforms(pProgram); 706 } 707 708 if (cbData<sizeof(GLsizei)) 709 { 710 crWarning("crStateGLSLProgramCacheUniforms: data too short"); 711 return; 712 } 713 714 pProgram->cUniforms = ((GLsizei*)pCurrent)[0]; 715 pCurrent += sizeof(GLsizei); 716 cbRead = sizeof(GLsizei); 717 718 crDebug("crStateGLSLProgramCacheUniforms: %i active uniforms", pProgram->cUniforms); 719 720 if (pProgram->cUniforms) 721 { 722 pProgram->pUniforms = crAlloc(pProgram->cUniforms*sizeof(CRGLSLUniform)); 723 if (!pProgram->pUniforms) 724 { 725 crWarning("crStateGLSLProgramCacheUniforms: no memory"); 726 pProgram->cUniforms = 0; 727 return; 728 } 729 } 730 731 for (i=0; i<pProgram->cUniforms; ++i) 732 { 733 cbRead += sizeof(GLuint)+sizeof(GLsizei); 734 if (cbRead>cbData) 735 { 736 crWarning("crStateGLSLProgramCacheUniforms: out of data reading uniform %i", i); 737 return; 738 } 739 pProgram->pUniforms[i].data = NULL; 740 pProgram->pUniforms[i].location = ((GLuint*)pCurrent)[0]; 741 pCurrent += sizeof(GLuint); 742 cbName = ((GLsizei*)pCurrent)[0]; 743 pCurrent += sizeof(GLsizei); 744 745 cbRead += cbName; 746 if (cbRead>cbData) 747 { 748 crWarning("crStateGLSLProgramCacheUniforms: out of data reading uniform's name %i", i); 749 return; 750 } 751 752 pProgram->pUniforms[i].name = crStrndup(pCurrent, cbName); 753 pCurrent += cbName; 754 755 crDebug("crStateGLSLProgramCacheUniforms: uniform[%i]=%d, %s", i, pProgram->pUniforms[i].location, pProgram->pUniforms[i].name); 756 } 757 758 pProgram->bUniformsSynced = GL_TRUE; 759 760 CRASSERT((pCurrent-((char*)pData))==cbRead); 761 CRASSERT(cbRead==cbData); 762 } 763 #else 764 DECLEXPORT(void) STATE_APIENTRY 765 crStateGLSLProgramCacheUniforms(GLuint program, GLsizei maxcbData, GLsizei *cbData, GLvoid *pData) 766 { 767 CRGLSLProgram *pProgram = crStateGetProgramObj(program); 768 GLint maxUniformLen, activeUniforms=0, fakeUniformsCount, i, j; 769 char *pCurrent = pData; 770 GLsizei cbWritten; 771 772 if (!pProgram) 773 { 774 crWarning("Unknown program %d", program); 775 return; 776 } 777 778 diff_api.GetProgramiv(pProgram->hwid, GL_ACTIVE_UNIFORM_MAX_LENGTH, &maxUniformLen); 779 diff_api.GetProgramiv(pProgram->hwid, GL_ACTIVE_UNIFORMS, &activeUniforms); 780 781 *cbData = 0; 782 783 cbWritten = sizeof(GLsizei); 784 if (cbWritten>maxcbData) 785 { 786 crWarning("crStateGLSLProgramCacheUniforms: buffer too small"); 787 return; 788 } 789 ((GLsizei*)pCurrent)[0] = activeUniforms; 790 fakeUniformsCount = activeUniforms; 791 pCurrent += sizeof(GLsizei); 792 793 crDebug("crStateGLSLProgramCacheUniforms: %i active uniforms", activeUniforms); 794 795 if (activeUniforms>0) 796 { 797 /*+8 to make sure our array uniforms with higher indices will fit in as well*/ 798 GLchar *name = (GLchar *) crAlloc(maxUniformLen+8); 799 GLenum type; 800 GLint size; 801 GLsizei cbName; 802 GLuint location; 803 804 if (!name) 805 { 806 crWarning("crStateGLSLProgramCacheUniforms: no memory"); 807 return; 808 } 809 810 for (i=0; i<activeUniforms; ++i) 811 { 812 diff_api.GetActiveUniform(pProgram->hwid, i, maxUniformLen, &cbName, &size, &type, name); 813 location = diff_api.GetUniformLocation(pProgram->hwid, name); 814 815 cbWritten += sizeof(GLuint)+sizeof(GLsizei)+cbName; 816 if (cbWritten>maxcbData) 817 { 818 crWarning("crStateGLSLProgramCacheUniforms: buffer too small"); 819 crFree(name); 820 return; 821 } 822 823 crDebug("crStateGLSLProgramCacheUniforms: uniform[%i]=%d, %s. size=%i", i, location, name, size); 824 825 ((GLuint*)pCurrent)[0] = location; 826 pCurrent += sizeof(GLuint); 827 ((GLsizei*)pCurrent)[0] = cbName; 828 pCurrent += sizeof(GLsizei); 829 crMemcpy(pCurrent, name, cbName); 830 pCurrent += cbName; 831 832 /* Only one active uniform variable will be reported for a uniform array by glGetActiveUniform, 833 * so we insert fake elements for other array elements. 834 */ 835 if (size!=1) 836 { 837 char *pIndexStr = crStrchr(name, '['); 838 fakeUniformsCount += size-1; 839 840 841 if (!pIndexStr) 842 { 843 crWarning("Array uniform name %s doesn't contain [!?"); 844 } 845 else 846 { 847 for (j=1; j<size; ++j) 848 { 849 sprintf(pIndexStr, "[%i]", j); 850 cbName = crStrlen(name); 851 852 location = diff_api.GetUniformLocation(pProgram->hwid, name); 853 crDebug("crStateGLSLProgramCacheUniforms: uniform[%i].[%i]=%d, %s", i, j, location, name); 854 855 cbWritten += sizeof(GLuint)+sizeof(GLsizei)+cbName; 856 if (cbWritten>maxcbData) 857 { 858 crWarning("crStateGLSLProgramCacheUniforms: buffer too small"); 859 crFree(name); 860 return; 861 } 862 863 ((GLuint*)pCurrent)[0] = location; 864 pCurrent += sizeof(GLuint); 865 ((GLsizei*)pCurrent)[0] = cbName; 866 pCurrent += sizeof(GLsizei); 867 crMemcpy(pCurrent, name, cbName); 868 pCurrent += cbName; 869 } 870 } 871 } 872 } 873 874 crFree(name); 875 } 876 877 if (fakeUniformsCount!=activeUniforms) 878 { 879 ((GLsizei*)pData)[0] = fakeUniformsCount; 880 crDebug("FakeCount %i", fakeUniformsCount); 881 } 882 883 *cbData = cbWritten; 884 885 CRASSERT((pCurrent-((char*)pData))==cbWritten); 886 } 887 #endif 888 889 DECLEXPORT(GLint) STATE_APIENTRY crStateGetUniformLocation(GLuint program, const char * name) 890 { 891 #ifdef IN_GUEST 892 CRGLSLProgram *pProgram = crStateGetProgramObj(program); 893 GLint result=-1; 894 GLuint i; 895 896 if (!pProgram) 897 { 898 crWarning("Unknown program %d", program); 899 return -1; 900 } 901 902 if (!pProgram->bUniformsSynced) 903 { 904 crWarning("crStateGetUniformLocation called for uncached uniforms"); 905 return -1; 906 } 907 908 for (i=0; i<pProgram->cUniforms; ++i) 909 { 910 if (!crStrcmp(name, pProgram->pUniforms[i].name)) 911 { 912 result = pProgram->pUniforms[i].location; 913 break; 914 } 915 } 916 917 return result; 918 #else 919 crWarning("crStateGetUniformLocation called on host side!!"); 920 return -1; 921 #endif 922 } 923 618 924 static void crStateGLSLCreateShadersCB(unsigned long key, void *data1, void *data2) 619 925 { -
trunk/src/VBox/GuestHost/OpenGL/state_tracker/state_snapshot.c
r23274 r23399 733 733 734 734 /*@todo check if we'd reference all array elements or not*/ 735 /*crap it fails as expected*/ 735 736 if (size!=1) crWarning("@todo"); 736 737 -
trunk/src/VBox/GuestHost/OpenGL/state_tracker/state_special
r23274 r23399 374 374 ValidateProgram 375 375 BindAttribLocation 376 GetUniformLocation -
trunk/src/VBox/HostServices/SharedOpenGL/crserverlib/server_getshaders.c
r23275 r23399 177 177 crServerReturnValue(&zero, sizeof(zero)); 178 178 } 179 cr_server.head_spu->dispatch_table.GetShaderSource(crStateGetShaderHWID(shader), bufSize, pLocal, (char*)pLocal+1); 180 crServerReturnValue(pLocal, (*pLocal)+1+sizeof(GLsizei)); 179 cr_server.head_spu->dispatch_table.GetShaderSource(crStateGetShaderHWID(shader), bufSize, pLocal, (char*)&pLocal[1]); 180 crServerReturnValue(pLocal, (*pLocal)+1+sizeof(GLsizei)); 181 crFree(pLocal); 182 } 183 184 void SERVER_DISPATCH_APIENTRY 185 crServerDispatchGetUniformsLocations(GLuint program, GLsizei maxcbData, GLsizei * cbData, GLvoid * pData) 186 { 187 GLsizei *pLocal; 188 189 (void) cbData; 190 (void) pData; 191 192 pLocal = (GLsizei*) crAlloc(maxcbData+sizeof(GLsizei)); 193 if (!pLocal) 194 { 195 GLsizei zero=0; 196 crServerReturnValue(&zero, sizeof(zero)); 197 } 198 199 crStateGLSLProgramCacheUniforms(program, maxcbData, pLocal, (char*)&pLocal[1]); 200 201 crServerReturnValue(pLocal, (*pLocal)+sizeof(GLsizei)); 181 202 crFree(pLocal); 182 203 } -
trunk/src/VBox/HostServices/SharedOpenGL/crserverlib/server_special
r23274 r23399 219 219 BindAttribLocation 220 220 DeleteObjectARB 221 GetUniformsLocations -
trunk/src/VBox/HostServices/SharedOpenGL/unpacker/unpack_shaders.c
r22535 r23399 262 262 cr_unpackDispatch.GetUniformLocation(program, name); 263 263 } 264 265 void crUnpackExtendGetUniformsLocations(void) 266 { 267 GLuint program = READ_DATA(8, GLuint); 268 GLsizei maxcbData = READ_DATA(12, GLsizei); 269 SET_RETURN_PTR(16); 270 SET_WRITEBACK_PTR(24); 271 cr_unpackDispatch.GetUniformsLocations(program, maxcbData, NULL, NULL); 272 } -
trunk/src/VBox/HostServices/SharedOpenGL/unpacker/unpacker_special
r22284 r23399 168 168 LockArraysEXT 169 169 UnlockArraysEXT 170 GetUniformsLocations
Note:
See TracChangeset
for help on using the changeset viewer.