From dbf59de6d2f8be526e97af6c768622e6ca3cf6b1 Mon Sep 17 00:00:00 2001 From: Pauli Nieminen Date: Wed, 26 Aug 2009 01:43:27 +0300 Subject: r200: Add scissor to state atom list. Scissors are jsut one of states that we have to emit so it should be in state list --- src/mesa/drivers/dri/r200/r200_cmdbuf.c | 121 ++++++++------------- src/mesa/drivers/dri/r200/r200_context.c | 1 + src/mesa/drivers/dri/r200/r200_context.h | 10 ++ src/mesa/drivers/dri/r200/r200_ioctl.h | 10 ++ src/mesa/drivers/dri/r200/r200_state.c | 24 ++++ src/mesa/drivers/dri/r200/r200_state.h | 2 + src/mesa/drivers/dri/r200/r200_state_init.c | 24 +++- src/mesa/drivers/dri/r200/r200_tcl.c | 1 - src/mesa/drivers/dri/radeon/radeon_common.c | 3 + .../drivers/dri/radeon/radeon_common_context.h | 1 + 10 files changed, 119 insertions(+), 78 deletions(-) (limited to 'src/mesa/drivers/dri') diff --git a/src/mesa/drivers/dri/r200/r200_cmdbuf.c b/src/mesa/drivers/dri/r200/r200_cmdbuf.c index 5f10279e56..1fe68c2b4c 100644 --- a/src/mesa/drivers/dri/r200/r200_cmdbuf.c +++ b/src/mesa/drivers/dri/r200/r200_cmdbuf.c @@ -49,6 +49,13 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. /* The state atoms will be emitted in the order they appear in the atom list, * so this step is important. */ +#define insert_at_tail_if(atom_list, atom) \ + do { \ + struct radeon_state_atom* __atom = (atom); \ + if (__atom->check) \ + insert_at_tail((atom_list), __atom); \ + } while(0) + void r200SetUpAtomList( r200ContextPtr rmesa ) { int i, mtu; @@ -58,86 +65,52 @@ void r200SetUpAtomList( r200ContextPtr rmesa ) make_empty_list(&rmesa->radeon.hw.atomlist); rmesa->radeon.hw.atomlist.name = "atom-list"; - insert_at_tail( &rmesa->radeon.hw.atomlist, &rmesa->hw.ctx ); - insert_at_tail( &rmesa->radeon.hw.atomlist, &rmesa->hw.set ); - insert_at_tail( &rmesa->radeon.hw.atomlist, &rmesa->hw.lin ); - insert_at_tail( &rmesa->radeon.hw.atomlist, &rmesa->hw.msk ); - insert_at_tail( &rmesa->radeon.hw.atomlist, &rmesa->hw.vpt ); - insert_at_tail( &rmesa->radeon.hw.atomlist, &rmesa->hw.vtx ); - insert_at_tail( &rmesa->radeon.hw.atomlist, &rmesa->hw.vap ); - insert_at_tail( &rmesa->radeon.hw.atomlist, &rmesa->hw.vte ); - insert_at_tail( &rmesa->radeon.hw.atomlist, &rmesa->hw.msc ); - insert_at_tail( &rmesa->radeon.hw.atomlist, &rmesa->hw.cst ); - insert_at_tail( &rmesa->radeon.hw.atomlist, &rmesa->hw.zbs ); - insert_at_tail( &rmesa->radeon.hw.atomlist, &rmesa->hw.tcl ); - insert_at_tail( &rmesa->radeon.hw.atomlist, &rmesa->hw.msl ); - insert_at_tail( &rmesa->radeon.hw.atomlist, &rmesa->hw.tcg ); - insert_at_tail( &rmesa->radeon.hw.atomlist, &rmesa->hw.grd ); - insert_at_tail( &rmesa->radeon.hw.atomlist, &rmesa->hw.fog ); - insert_at_tail( &rmesa->radeon.hw.atomlist, &rmesa->hw.tam ); - insert_at_tail( &rmesa->radeon.hw.atomlist, &rmesa->hw.tf ); - insert_at_tail( &rmesa->radeon.hw.atomlist, &rmesa->hw.atf ); + insert_at_tail_if( &rmesa->radeon.hw.atomlist, &rmesa->hw.ctx ); + insert_at_tail_if( &rmesa->radeon.hw.atomlist, &rmesa->hw.set ); + insert_at_tail_if( &rmesa->radeon.hw.atomlist, &rmesa->hw.lin ); + insert_at_tail_if( &rmesa->radeon.hw.atomlist, &rmesa->hw.msk ); + insert_at_tail_if( &rmesa->radeon.hw.atomlist, &rmesa->hw.vpt ); + insert_at_tail_if( &rmesa->radeon.hw.atomlist, &rmesa->hw.vtx ); + insert_at_tail_if( &rmesa->radeon.hw.atomlist, &rmesa->hw.vap ); + insert_at_tail_if( &rmesa->radeon.hw.atomlist, &rmesa->hw.vte ); + insert_at_tail_if( &rmesa->radeon.hw.atomlist, &rmesa->hw.msc ); + insert_at_tail_if( &rmesa->radeon.hw.atomlist, &rmesa->hw.cst ); + insert_at_tail_if( &rmesa->radeon.hw.atomlist, &rmesa->hw.zbs ); + insert_at_tail_if( &rmesa->radeon.hw.atomlist, &rmesa->hw.tcl ); + insert_at_tail_if( &rmesa->radeon.hw.atomlist, &rmesa->hw.msl ); + insert_at_tail_if( &rmesa->radeon.hw.atomlist, &rmesa->hw.tcg ); + insert_at_tail_if( &rmesa->radeon.hw.atomlist, &rmesa->hw.grd ); + insert_at_tail_if( &rmesa->radeon.hw.atomlist, &rmesa->hw.fog ); + insert_at_tail_if( &rmesa->radeon.hw.atomlist, &rmesa->hw.tam ); + insert_at_tail_if( &rmesa->radeon.hw.atomlist, &rmesa->hw.tf ); + insert_at_tail_if( &rmesa->radeon.hw.atomlist, &rmesa->hw.atf ); for (i = 0; i < mtu; ++i) - insert_at_tail( &rmesa->radeon.hw.atomlist, &rmesa->hw.tex[i] ); + insert_at_tail_if( &rmesa->radeon.hw.atomlist, &rmesa->hw.tex[i] ); for (i = 0; i < mtu; ++i) - insert_at_tail( &rmesa->radeon.hw.atomlist, &rmesa->hw.cube[i] ); + insert_at_tail_if( &rmesa->radeon.hw.atomlist, &rmesa->hw.cube[i] ); for (i = 0; i < 6; ++i) - insert_at_tail( &rmesa->radeon.hw.atomlist, &rmesa->hw.pix[i] ); - insert_at_tail( &rmesa->radeon.hw.atomlist, &rmesa->hw.afs[0] ); - insert_at_tail( &rmesa->radeon.hw.atomlist, &rmesa->hw.afs[1] ); + insert_at_tail_if( &rmesa->radeon.hw.atomlist, &rmesa->hw.pix[i] ); + insert_at_tail_if( &rmesa->radeon.hw.atomlist, &rmesa->hw.afs[0] ); + insert_at_tail_if( &rmesa->radeon.hw.atomlist, &rmesa->hw.afs[1] ); for (i = 0; i < 8; ++i) - insert_at_tail( &rmesa->radeon.hw.atomlist, &rmesa->hw.lit[i] ); + insert_at_tail_if( &rmesa->radeon.hw.atomlist, &rmesa->hw.lit[i] ); for (i = 0; i < 3 + mtu; ++i) - insert_at_tail( &rmesa->radeon.hw.atomlist, &rmesa->hw.mat[i] ); - insert_at_tail( &rmesa->radeon.hw.atomlist, &rmesa->hw.eye ); - insert_at_tail( &rmesa->radeon.hw.atomlist, &rmesa->hw.glt ); + insert_at_tail_if( &rmesa->radeon.hw.atomlist, &rmesa->hw.mat[i] ); + insert_at_tail_if( &rmesa->radeon.hw.atomlist, &rmesa->hw.eye ); + insert_at_tail_if( &rmesa->radeon.hw.atomlist, &rmesa->hw.glt ); for (i = 0; i < 2; ++i) - insert_at_tail( &rmesa->radeon.hw.atomlist, &rmesa->hw.mtl[i] ); + insert_at_tail_if( &rmesa->radeon.hw.atomlist, &rmesa->hw.mtl[i] ); for (i = 0; i < 6; ++i) - insert_at_tail( &rmesa->radeon.hw.atomlist, &rmesa->hw.ucp[i] ); - insert_at_tail( &rmesa->radeon.hw.atomlist, &rmesa->hw.spr ); - insert_at_tail( &rmesa->radeon.hw.atomlist, &rmesa->hw.ptp ); - insert_at_tail( &rmesa->radeon.hw.atomlist, &rmesa->hw.prf ); - insert_at_tail( &rmesa->radeon.hw.atomlist, &rmesa->hw.pvs ); - insert_at_tail( &rmesa->radeon.hw.atomlist, &rmesa->hw.vpp[0] ); - insert_at_tail( &rmesa->radeon.hw.atomlist, &rmesa->hw.vpp[1] ); - insert_at_tail( &rmesa->radeon.hw.atomlist, &rmesa->hw.vpi[0] ); - insert_at_tail( &rmesa->radeon.hw.atomlist, &rmesa->hw.vpi[1] ); -} - -static void r200EmitScissor(r200ContextPtr rmesa) -{ - unsigned x1, y1, x2, y2; - struct radeon_renderbuffer *rrb; - BATCH_LOCALS(&rmesa->radeon); - if (!rmesa->radeon.radeonScreen->kernel_mm) { - return; - } - rrb = radeon_get_colorbuffer(&rmesa->radeon); - if (!rrb || !rrb->bo) - return; - - if (rmesa->radeon.state.scissor.enabled) { - x1 = rmesa->radeon.state.scissor.rect.x1; - y1 = rmesa->radeon.state.scissor.rect.y1; - x2 = rmesa->radeon.state.scissor.rect.x2 - 1; - y2 = rmesa->radeon.state.scissor.rect.y2 - 1; - } else { - x1 = 0; - y1 = 0; - x2 = rrb->base.Width - 1; - y2 = rrb->base.Height - 1; - } - BEGIN_BATCH(8); - OUT_BATCH(CP_PACKET0(R200_RE_CNTL, 0)); - OUT_BATCH(R200_SCISSOR_ENABLE | rmesa->hw.set.cmd[SET_RE_CNTL]); - OUT_BATCH(CP_PACKET0(R200_RE_AUX_SCISSOR_CNTL, 0)); - OUT_BATCH(0); - OUT_BATCH(CP_PACKET0(R200_RE_TOP_LEFT, 0)); - OUT_BATCH((y1 << 16) | x1); - OUT_BATCH(CP_PACKET0(R200_RE_WIDTH_HEIGHT, 0)); - OUT_BATCH((y2 << 16) | x2); - END_BATCH(); + insert_at_tail_if( &rmesa->radeon.hw.atomlist, &rmesa->hw.ucp[i] ); + insert_at_tail_if( &rmesa->radeon.hw.atomlist, &rmesa->hw.spr ); + insert_at_tail_if( &rmesa->radeon.hw.atomlist, &rmesa->hw.ptp ); + insert_at_tail_if( &rmesa->radeon.hw.atomlist, &rmesa->hw.prf ); + insert_at_tail_if( &rmesa->radeon.hw.atomlist, &rmesa->hw.pvs ); + insert_at_tail_if( &rmesa->radeon.hw.atomlist, &rmesa->hw.vpp[0] ); + insert_at_tail_if( &rmesa->radeon.hw.atomlist, &rmesa->hw.vpp[1] ); + insert_at_tail_if( &rmesa->radeon.hw.atomlist, &rmesa->hw.vpi[0] ); + insert_at_tail_if( &rmesa->radeon.hw.atomlist, &rmesa->hw.vpi[1] ); + insert_at_tail_if( &rmesa->radeon.hw.atomlist, &rmesa->hw.sci ); } /* Fire a section of the retained (indexed_verts) buffer as a regular @@ -156,7 +129,6 @@ void r200EmitVbufPrim( r200ContextPtr rmesa, if (R200_DEBUG & (DEBUG_IOCTL|DEBUG_PRIMS)) fprintf(stderr, "%s cmd_used/4: %d prim %x nr %d\n", __FUNCTION__, rmesa->store.cmd_used/4, primitive, vertex_nr); - r200EmitScissor(rmesa); BEGIN_BATCH(3); OUT_BATCH_PACKET3_CLIP(R200_CP_CMD_3D_DRAW_VBUF_2, 0); @@ -170,7 +142,6 @@ static void r200FireEB(r200ContextPtr rmesa, int vertex_count, int type) BATCH_LOCALS(&rmesa->radeon); if (vertex_count > 0) { - r200EmitScissor(rmesa); BEGIN_BATCH(8+2); OUT_BATCH_PACKET3_CLIP(R200_CP_CMD_3D_DRAW_INDX_2, 0); OUT_BATCH(R200_VF_PRIM_WALK_IND | diff --git a/src/mesa/drivers/dri/r200/r200_context.c b/src/mesa/drivers/dri/r200/r200_context.c index 8cb287de26..d2594d7d16 100644 --- a/src/mesa/drivers/dri/r200/r200_context.c +++ b/src/mesa/drivers/dri/r200/r200_context.c @@ -270,6 +270,7 @@ static void r200_init_vtbl(radeonContextPtr radeon) radeon->vtbl.emit_cs_header = r200_vtbl_emit_cs_header; radeon->vtbl.swtcl_flush = r200_swtcl_flush; radeon->vtbl.fallback = r200Fallback; + radeon->vtbl.update_scissor = r200_vtbl_update_scissor; } diff --git a/src/mesa/drivers/dri/r200/r200_context.h b/src/mesa/drivers/dri/r200/r200_context.h index 18360890eb..c1cb0dd20c 100644 --- a/src/mesa/drivers/dri/r200/r200_context.h +++ b/src/mesa/drivers/dri/r200/r200_context.h @@ -467,6 +467,15 @@ struct r200_texture_state { #define PRF_STATE_SIZE 3 +#define SCI_CMD_0 0 +#define SCI_RE_AUX 1 +#define SCI_CMD_1 2 +#define SCI_XY_1 3 +#define SCI_CMD_2 4 +#define SCI_XY_2 5 +#define SCI_STATE_SIZE 6 + + struct r200_hw_state { /* Hardware state, stored as cmdbuf commands: * -- Need to doublebuffer for @@ -475,6 +484,7 @@ struct r200_hw_state { */ struct radeon_state_atom ctx; struct radeon_state_atom set; + struct radeon_state_atom sci; struct radeon_state_atom vte; struct radeon_state_atom lin; struct radeon_state_atom msk; diff --git a/src/mesa/drivers/dri/r200/r200_ioctl.h b/src/mesa/drivers/dri/r200/r200_ioctl.h index f6419f5a2c..9f06d23b38 100644 --- a/src/mesa/drivers/dri/r200/r200_ioctl.h +++ b/src/mesa/drivers/dri/r200/r200_ioctl.h @@ -98,6 +98,16 @@ do { \ rmesa->radeon.hw.is_dirty = GL_TRUE; \ } while (0) +#define R200_SET_STATE( rmesa, ATOM, index, newvalue ) \ + do { \ + uint32_t __index = (index); \ + uint32_t __dword = (newvalue); \ + if (__dword != (rmesa)->hw.ATOM.cmd[__index]) { \ + R200_STATECHANGE( (rmesa), ATOM ); \ + (rmesa)->hw.ATOM.cmd[__index] = __dword; \ + } \ + } while(0) + #define R200_DB_STATE( ATOM ) \ memcpy( rmesa->hw.ATOM.lastcmd, rmesa->hw.ATOM.cmd, \ rmesa->hw.ATOM.cmd_size * 4) diff --git a/src/mesa/drivers/dri/r200/r200_state.c b/src/mesa/drivers/dri/r200/r200_state.c index ffc1a95745..250b4358c9 100644 --- a/src/mesa/drivers/dri/r200/r200_state.c +++ b/src/mesa/drivers/dri/r200/r200_state.c @@ -1650,6 +1650,30 @@ void r200UpdateWindow( GLcontext *ctx ) rmesa->hw.vpt.cmd[VPT_SE_VPORT_ZOFFSET] = tz.ui32; } +void r200_vtbl_update_scissor( GLcontext *ctx ) +{ + r200ContextPtr r200 = R200_CONTEXT(ctx); + unsigned x1, y1, x2, y2; + struct radeon_renderbuffer *rrb; + + R200_SET_STATE(r200, set, SET_RE_CNTL, R200_SCISSOR_ENABLE | r200->hw.set.cmd[SET_RE_CNTL]); + + if (r200->radeon.state.scissor.enabled) { + x1 = r200->radeon.state.scissor.rect.x1; + y1 = r200->radeon.state.scissor.rect.y1; + x2 = r200->radeon.state.scissor.rect.x2 - 1; + y2 = r200->radeon.state.scissor.rect.y2 - 1; + } else { + rrb = radeon_get_colorbuffer(&r200->radeon); + x1 = 0; + y1 = 0; + x2 = rrb->base.Width - 1; + y2 = rrb->base.Height - 1; + } + + R200_SET_STATE(r200, sci, SCI_XY_1, x1 | (y1 << 16)); + R200_SET_STATE(r200, sci, SCI_XY_2, x2 | (y2 << 16)); +} static void r200Viewport( GLcontext *ctx, GLint x, GLint y, diff --git a/src/mesa/drivers/dri/r200/r200_state.h b/src/mesa/drivers/dri/r200/r200_state.h index 23cf8aea66..7b9b0c106a 100644 --- a/src/mesa/drivers/dri/r200/r200_state.h +++ b/src/mesa/drivers/dri/r200/r200_state.h @@ -49,6 +49,8 @@ extern void r200UpdateDrawBuffer(GLcontext *ctx); extern GLboolean r200ValidateState( GLcontext *ctx ); +extern void r200_vtbl_update_scissor( GLcontext *ctx ); + extern void r200Fallback( GLcontext *ctx, GLuint bit, GLboolean mode ); #define FALLBACK( rmesa, bit, mode ) do { \ if ( 0 ) fprintf( stderr, "FALLBACK in %s: #%d=%d\n", \ diff --git a/src/mesa/drivers/dri/r200/r200_state_init.c b/src/mesa/drivers/dri/r200/r200_state_init.c index 9b443bd0ea..42d66258c3 100644 --- a/src/mesa/drivers/dri/r200/r200_state_init.c +++ b/src/mesa/drivers/dri/r200/r200_state_init.c @@ -340,6 +340,15 @@ VP_CHECK( tcl_vpp_size_add4, ctx->VertexProgram.Current->Base.NumNativeParameter OUT_BATCH(CP_PACKET0_ONE(R200_SE_TCL_SCALAR_DATA_REG, h.scalars.count - 1)); \ OUT_BATCH_TABLE((data), h.scalars.count); \ } while(0) +static int check_rrb(GLcontext *ctx, struct radeon_state_atom *atom) +{ + r200ContextPtr r200 = R200_CONTEXT(ctx); + struct radeon_renderbuffer *rrb; + rrb = radeon_get_colorbuffer(&r200->radeon); + if (!rrb || !rrb->bo) + return 0; + return atom->cmd_size; +} static void mtl_emit(GLcontext *ctx, struct radeon_state_atom *atom) { @@ -792,9 +801,13 @@ void r200InitState( r200ContextPtr rmesa ) rmesa->hw.ATOM.lastcmd = (GLuint *)CALLOC(SZ * sizeof(int)); \ rmesa->hw.ATOM.name = NM; \ rmesa->hw.ATOM.idx = IDX; \ - rmesa->hw.ATOM.check = check_##CHK; \ + if (check_##CHK != check_never) { \ + rmesa->hw.ATOM.check = check_##CHK; \ + rmesa->radeon.hw.max_state_size += SZ * sizeof(int); \ + } else { \ + rmesa->hw.ATOM.check = NULL; \ + } \ rmesa->hw.ATOM.dirty = GL_FALSE; \ - rmesa->radeon.hw.max_state_size += SZ * sizeof(int); \ } while (0) @@ -955,6 +968,7 @@ void r200InitState( r200ContextPtr rmesa ) ALLOC_STATE( lit[5], tcl_light_add8, LIT_STATE_SIZE, "LIT/light-5", 5 ); ALLOC_STATE( lit[6], tcl_light_add8, LIT_STATE_SIZE, "LIT/light-6", 6 ); ALLOC_STATE( lit[7], tcl_light_add8, LIT_STATE_SIZE, "LIT/light-7", 7 ); + ALLOC_STATE( sci, rrb, SCI_STATE_SIZE, "SCI/scissor", 0 ); } else { ALLOC_STATE( mtl[0], tcl_lighting, MTL_STATE_SIZE, "MTL0/material0", 0 ); ALLOC_STATE( mtl[1], tcl_lighting, MTL_STATE_SIZE, "MTL1/material1", 1 ); @@ -985,6 +999,7 @@ void r200InitState( r200ContextPtr rmesa ) ALLOC_STATE( lit[5], tcl_light, LIT_STATE_SIZE, "LIT/light-5", 5 ); ALLOC_STATE( lit[6], tcl_light, LIT_STATE_SIZE, "LIT/light-6", 6 ); ALLOC_STATE( lit[7], tcl_light, LIT_STATE_SIZE, "LIT/light-7", 7 ); + ALLOC_STATE( sci, never, SCI_STATE_SIZE, "SCI/scissor", 0 ); } ALLOC_STATE( pix[0], pix_zero, PIX_STATE_SIZE, "PIX/pixstage-0", 0 ); ALLOC_STATE( pix[1], texenv, PIX_STATE_SIZE, "PIX/pixstage-1", 1 ); @@ -1095,6 +1110,11 @@ void r200InitState( r200ContextPtr rmesa ) rmesa->hw.vte.cmd[VTE_CMD_0] = cmdpkt(rmesa, R200_EMIT_VTE_CNTL); rmesa->hw.prf.cmd[PRF_CMD_0] = cmdpkt(rmesa, R200_EMIT_PP_TRI_PERF_CNTL); rmesa->hw.spr.cmd[SPR_CMD_0] = cmdpkt(rmesa, R200_EMIT_TCL_POINT_SPRITE_CNTL); + + rmesa->hw.sci.cmd[SCI_CMD_0] = CP_PACKET0(R200_RE_AUX_SCISSOR_CNTL, 0); + rmesa->hw.sci.cmd[SCI_CMD_1] = CP_PACKET0(R200_RE_TOP_LEFT, 0); + rmesa->hw.sci.cmd[SCI_CMD_2] = CP_PACKET0(R200_RE_WIDTH_HEIGHT, 0); + if (rmesa->radeon.radeonScreen->kernel_mm) { rmesa->hw.mtl[0].emit = mtl_emit; rmesa->hw.mtl[1].emit = mtl_emit; diff --git a/src/mesa/drivers/dri/r200/r200_tcl.c b/src/mesa/drivers/dri/r200/r200_tcl.c index af528ae481..0f35d4d5b1 100644 --- a/src/mesa/drivers/dri/r200/r200_tcl.c +++ b/src/mesa/drivers/dri/r200/r200_tcl.c @@ -413,7 +413,6 @@ static GLuint r200EnsureEmitSize( GLcontext * ctx , GLubyte* vimap_rev ) else space_required += index + elts; space_required += AOS_BUFSZ(nr_aos); - space_required += SCISSOR_BUFSZ; } } diff --git a/src/mesa/drivers/dri/radeon/radeon_common.c b/src/mesa/drivers/dri/radeon/radeon_common.c index 8f34fbf6bc..e0be15fdef 100644 --- a/src/mesa/drivers/dri/radeon/radeon_common.c +++ b/src/mesa/drivers/dri/radeon/radeon_common.c @@ -148,6 +148,9 @@ void radeonRecalcScissorRects(radeonContextPtr radeon) out++; } } + + if (radeon->vtbl.update_scissor) + radeon->vtbl.update_scissor(radeon->glCtx); } void radeon_get_cliprects(radeonContextPtr radeon, diff --git a/src/mesa/drivers/dri/radeon/radeon_common_context.h b/src/mesa/drivers/dri/radeon/radeon_common_context.h index 9e9c35650d..cb47484de1 100644 --- a/src/mesa/drivers/dri/radeon/radeon_common_context.h +++ b/src/mesa/drivers/dri/radeon/radeon_common_context.h @@ -548,6 +548,7 @@ struct radeon_context { void (*fallback)(GLcontext *ctx, GLuint bit, GLboolean mode); void (*free_context)(GLcontext *ctx); void (*emit_query_finish)(radeonContextPtr radeon); + void (*update_scissor)(GLcontext *ctx); } vtbl; }; -- cgit v1.2.3